2011-03-14 92 views
9

我正在尝试制作一个玩具应用程序,只是为了让我的头了解如何在Haskell中编写事件驱动的程序。我想要做的是在每次按下按键时向前移动一个画布(因此它是文本编辑器中的原始光标)。如何在gtk2hs中的事件处理程序之间传递状态

我的问题是我无法计算出用户按下某个键的次数的最佳方法。显然,我不能像我在一个命令式程序中那样使用全局变量,所以大概我需要在调用堆栈上传递状态,但是在GTK中,每个事件处理程序返回后执行都会下降到主循环中,并且由于我不控制主循环我看不出我如何从一个事件处理程序传递更改后的全局状态。那么,一个事件处理程序如何将状态传递给另一个事件处理程序

我在这里有一种部分解决方案,其中键盘事件re-curries 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 

回答

7

首先,你可以有一个全球性的。忽略该解决方案为糟糕的形式,这看起来像是MVar的工作。在main你只是做一个新的无功,你可以在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 
    ... 

注意,共享可变状态首先使用部分应用提供MVar(传递函数作为参数的时候往往也是有用或者TVar,IORef,无论如何)。

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

相关问题