2011-03-14 96 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。

相關問題