在Gtk2Hs中使用DrawingArea的动画

时间:2013-01-02 05:12:31

标签: haskell gtk2hs

我在Haskell方面没那么有经验,我刚刚开始使用Gtk2Hs,所以这可能是一个愚蠢的问题。

我定义了以下Line类型:

type Coord = (Int,Int)
type Line = (Coord,Coord)

我有一个函数可以在Line上绘制DrawingArea的列表。问题是这个函数同时绘制了所有Line,但我想一次绘制一个Line之间有一点延迟。

render :: [Line] -> IO ()
render lines =
  do initGUI
     win <- windowNew
     windowSetTitle win "Animation"
     win `onDestroy` mainQuit

     can <- drawingAreaNew
     can `onSizeRequest` return (Requisition 400 400)
     can `onExpose` drawCanvas lines can

     but <- buttonNewWithLabel "Quit"
     but `onClicked` mainQuit
     hbox <- hBoxNew False 0
     boxPackStart hbox but PackRepel 150

     vbox <- vBoxNew False 5
     containerAdd vbox can
     containerAdd vbox hbox
     containerAdd win vbox

     widgetShowAll win
     mainGUI

在公开DrawingArea时调用此函数:

drawCanvas :: [Line] -> DrawingArea -> event -> IO Bool
drawCanvas lines can _evt =
  do dw <- widgetGetDrawWindow can
     drawWindowClear dw
     gc <- gcNew dw
     mapM_ (\(a,b) -> drawLine dw gc a b) lines
     return True

我考虑使用StateT来跟踪尚未绘制的Line但我不知道如何实现动画。即使在改变状态后每次调用widgetShowAll,在调用mainGUI之前窗口也不会显示。

是否有可能创建一个不断更新状态的新线程,drawCanvas以某种方式处理绘图?如果是这样,有人可以告诉我这种行为的一个例子吗?或者是否有更好的方法?

1 个答案:

答案 0 :(得分:3)

Gtk2Hs允许设置定时调用函数的“定时器”。所以我会做以下事情:

  • 因为大多数Gtk2H发生在IO monad中,所以使用IORef或MVar来存储动画的状态并在任何地方进行更改。
  • 在mainGUI之前添加对timeoutAdd的调用,如下所示:timeoutAdd (update can lines) 100设置一个每100毫秒运行一次的计时器,并使用DrawingArea和IORef / MVar调用新函数“update”将动画状态作为参数。
  • 在“更新”功能中,更改动画状态并调用widgetQueueDraw can,以便重新显示绘图区域。这将自动调用“drawCanvas”,因为它连接到expose事件。 “更新”必须返回IO Bool。返回False将停止计时器。
  • 要更新动画状态,我会使用元组。第一个元素将存储要绘制的行,第二个元素将存储其他行的列表。

我发现在单独的函数中更新动画的状态和绘图是有意义的。