2011-03-14 17 views
9

Haskell'de olay odaklı programların nasıl yazılacağı konusunda kafamı almak için bir oyuncak uygulaması yapmaya çalışıyorum. Yapmaya çalıştığım şey, bir tuşa her basıldığında ileriye doğru hareket eden bir tuvale çizgi çizmek (böylece bir metin editöründe ilkel bir imleç).gtk2hs içindeki olay işleyicileri arasındaki durum nasıl iletilir

Sorunum, kullanıcının bir tuşa basma sayısını saymanın en iyi yolunun ne olduğunu anlayamıyorum. Açıkçası, zorunlu bir programda olduğu gibi global bir değişken kullanamıyorum, bu yüzden muhtemelen çağrı yığınında durumu iletmem gerekiyor, ancak GTK uygulamasında her olay işleyicisi döndükten sonra ana döngüye iner ve Ana döngüyü kontrol ediyorum Değişen global durumu bir olay işleyicisinden nasıl geçebileceğimi göremiyorum. Peki, bir olay işleyicisi nasıl bir durumdan başka bir olay işleyicisine geçebilir?

Burada klavye olayının myDraw'ı yeniden körleştirdiği ve bunu olay işleyicisi olarak yeni ayarladığı bir çeşit kısmi çözümüm var. Bu çözümün genişletilip genişletilemeyeceği ya da iyi bir fikir olsa bile emin değilim.

Bu sorunun en iyi parçacık çözümü nedir?

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 

cevap

7

İlk önce bir global olabilir. Bu çözümü kötü bir form olarak görmezden gelmek, bu bir MVar için bir işe benziyor. main size sadece onKeyboard güncelleyebileceği yeni MVAr yapıp myDraw check-in: İlk (MVar sağlamak üzere kısmi uygulamayı kullanarak argümanları gibi işlevleri geçerken değişken durumunu paylaşımı da genellikle yararlı olduğunu

... 
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 
    ... 

Not veya TVar, IORef, ne olursa olsun).

Oh, ve bir uyarı: MVars katı değildir - eğer uygulama değeri zorlamadan (yani, Int'u okuyarak ve karşılaştırarak) bir sürü yazma potansiyeline sahipse, inşa etmekten kaçınmak için yazmadan önce değeri zorlamalısınız. çok büyük bir şey.

İlgili konular