8

Haskell でイベント駆動型プログラムを作成する方法を理解するためだけに、おもちゃのアプリケーションを作成しようとしています。私がやろうとしているのは、キーが押されるたびに前方に移動するキャンバスに線を引くことです (つまり、テキスト エディターの原始的なカーソルのようなものです)。

私の問題は、ユーザーがキーを押した回数を数える最良の方法を理解できないことです。明らかに、命令型プログラムのようにグローバル変数を使用することはできないため、おそらくコールスタックで状態を渡す必要がありますが、GTK では、各イベントハンドラーが返された後、実行がメインループに降ります。メイン ループを制御する 変更されたグローバル状態を 1 つのイベント ハンドラーから渡す方法がわかりません。では、あるイベント ハンドラーが別のイベント ハンドラーに状態を渡すにはどうすればよいのでしょうか。

ここでは、キーボード イベントが 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 
4

1 に答える 1

6

まず、グローバルを持つことができます。その解決策を悪い形として無視すると、これはMVarの仕事のように見えます。更新してチェックインできるmain新しい MVar を作成するだけです。onKeyboardmyDraw

...
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場合)、巨大なサンクを構築しないように、書き込む前に値を強制する必要があります。

于 2011-03-14T06:12:54.207 に答える