如何在gtk2hs中的事件处理程序之间传递状态

Gar*_*ock 8 haskell gtk2hs

我正在尝试制作玩具应用程序,以便了解如何在Haskell中编写事件驱动程序.我要做的是在画布上绘制一条线,每次按下一个键时向前移动(所以它在文本编辑器中是一种原始光标).

我的问题是我无法确定用户按下按键的次数的最佳方法.显然,我不能像在命令式程序中那样使用全局变量,所以我可能需要在调用堆栈上传递状态,但是在每个事件处理程序返回后,GTK执行会进入主循环,因为我不会控制主循环我不知道如何从一个事件处理程序传递更改的全局状态.那么一个事件处理程序如何将状态传递给另一个事件处理程序?

我在这里有一种部分解决方案,其中键盘事件重新调整myDraw,并将其设置为新的事件处理程序.我不确定这个解决方案是否可以扩展,或者即使这是一个好主意.

什么是这个问题的最佳粒子解决方案?

import Graphics.UI.Gtk
import Graphics.Rendering.Cairo

main :: IO ()
main= do
     initGUI
     window <- windowNew
     set window [windowTitle := "Hello World",
                 windowDefaultWidth := 300, windowDefaultHeight := 200]

     canvas <- drawingAreaNew
     containerAdd window canvas

     widgetShowAll window 
     draWin <- widgetGetDrawWindow canvas
     canvas `on` exposeEvent $ do liftIO $ renderWithDrawable draWin (myDraw 10)
                                  return False

     window `on` keyPressEvent $ onKeyboard canvas
     window `on` destroyEvent  $ do liftIO mainQuit
                                    return False

     mainGUI

onKeyboard :: DrawingArea -> EventM EKey Bool
onKeyboard canvas = do 
  liftIO $ do drawWin <- widgetGetDrawWindow canvas
              canvas `on` exposeEvent $ do liftIO $renderWithDrawable drawWin (myDraw 20)
                                           return False
              widgetQueueDraw canvas
  return False



myDraw :: Double -> Render ()
myDraw pos = do
    setSourceRGB 1 1 1
    paint
    setSourceRGB 0 0 0

    moveTo pos 0
    lineTo pos 20
    stroke 
Run Code Online (Sandbox Code Playgroud)

Tho*_*son 6

首先,你可以有一个全球性的.将该解决方案视为错误形式,这看起来像是一个MVar的工作.在main你刚刚创建一个新的MVar,你可以更新onKeyboard并签入myDraw:

...
import Control.Concurrent.MVar

main = do
    ...
    mv <- newMVar 0
    ....
    canvas `on` exposeEvent $ do liftIO $ renderWithDrawable draWin (myDraw mv 10)
    ...
    window `on` keyPressEvent $ onKeyboard canvas mv

onKeyboard canvas mv = do
    modifyMVar_ mv (\x -> return (x + 1))
    ....

myDraw mv pos = do
    val <- readMVar mv
    ...
Run Code Online (Sandbox Code Playgroud)

需要注意的是共享可变状态通过首先使用部分应用程序,以提供传递函数作为参数时,也常常是有用的MVar(或TVar,IORef,等等).

哦,还有一个警告:MVars并不严格 - 如果应用程序有可能在不强制值的情况下编写批量(即读取和比较包含Int的值),那么您应该在写入之前强制该值以避免构建大量的thunk.