1

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

Line定义了以下类型:

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

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

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

我考虑过使用 aStateT来跟踪哪些Lines 尚未绘制,但我不知道如何实现动画。即使widgetShowAll每次更改状态后调用,窗口也不会显示,直到mainGUI被调用。

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

4

1 回答 1

3

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

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

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

于 2013-01-02T09:44:26.467 回答