Как передать состояние между обработчиками событий в gtk2hs - PullRequest
8 голосов
/ 14 марта 2011

Я пытаюсь создать игрушечное приложение, просто чтобы понять, как писать управляемые событиями программы на 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 

1 Ответ

6 голосов
/ 14 марта 2011

Во-первых, вы можете иметь глобальный.Игнорируя это решение как плохую форму, это выглядит как работа для 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
    ...

Обратите внимание, что совместное использование изменяемого состояния также часто полезно при передаче функций в качестве аргументов первымиспользование частичного приложения для предоставления MVar (или TVar, IORef, как угодно).

Да, и предупреждение: MVars не строги - если у приложения есть потенциал для написания лотов безфорсируя значение (т. е. читая и сравнивая содержащиеся Int), вы должны форсировать значение перед записью, чтобы избежать создания огромного потока.

...