我使用WxHaskell以图形方式显示一个程序的状态,该程序使用TCP (我使用Data.Binary解码)通告状态更新。当收到更新时,我想要更新显示。所以我希望GUI异步更新它的显示。我知道processExecAsync异步运行命令行进程,但我不认为这是我想要的。
发布于 2010-07-06 02:32:24
这是使用事务变量(即软件事务内存)的粗略代码。您可以使用IORef、MVar或许多其他构造。
main = do
recvFunc <- initNetwork
cntTV <- newTVarIO 0
forkIO $ threadA recvFunc cntTV
runGUI cntTV 0在上面启动程序,初始化网络和一个共享变量cntTV
threadA recvCntFromNetwork cntTVar = forever $ do
cnt <- recvCntFromNetwork
atomically (writeTVar cntTVar cnt)threadA从网络接收数据,并将计数器的新值写入共享变量。
runGUI cntTVar currentCnt = do
counter <- initGUI
cnt <- atomically $ do
cnt <- readTVar cntTVar
if (cnt == currentCnt)
then retry
else return cnt
updateGUICounter counter cnt
runGUI cntTVar cntrunGUI读取共享变量,如果有更改,将更新图形用户界面计数器。仅供参考,CPU线程不会在retry上唤醒,直到cntTVar被修改,所以这不是一个runGUI独占轮询循环。
在这段代码中,我假设您有名为updateGUICounter、initGUI和initNetwork的函数。我建议您使用Hoogle查找您不知道的任何其他函数的位置,并了解一些关于每个模块的信息。
发布于 2010-07-06 07:03:08
我想出了一种似乎有效的黑客方法。即,使用事件定时器检查更新队列:
startClient :: IO (TVar [Update])
startClient = /*Connect to server,
listen for updates and add to queue*/
gui :: TVar [Update] -> IO ()
gui trdl = do
f <- frame [text := "counter", visible := False]
p <- panel f []
st <- staticText p []
t <- timer f [interval := 10, on command := updateGui st]
set f [layout := container p $ fill $ widget st, clientSize := (sz 200 100), visible := True]
where
updateGui st = do
rdl <- atomically $ readTVar trdl
atomically $ writeTVar trdl []
case rdl of
[] -> return ()
dat : dl -> set st [text := (show dat)]
main :: IO ()
main = startClient >>= start gui因此,客户端侦听TCP连接上的更新,将它们添加到队列中。每隔10ms,就会引发一个事件,该事件的操作是检查此队列并在静态文本小部件中显示最新更新。
如果你有更好的解决方案,请让我知道!
发布于 2012-09-26 15:22:46
我在http://snipplr.com/view/17538/上找到了一个没有忙碌等待的解决方案
但是,您可以选择更高的eventId,以避免与现有ids冲突。
下面是我的模块http://code.haskell.org/alsa/gui/src/Common.hs中的一些代码
myEventId :: Int
myEventId = WXCore.wxID_HIGHEST+100
-- the custom event ID, avoid clash with Graphics.UI.WXCore.Types.varTopId
-- | the custom event is registered as a menu event
createMyEvent :: IO (WXCore.CommandEvent ())
createMyEvent =
WXCore.commandEventCreate WXCore.wxEVT_COMMAND_MENU_SELECTED myEventId
registerMyEvent :: WXCore.EvtHandler a -> IO () -> IO ()
registerMyEvent win io =
WXCore.evtHandlerOnMenuCommand win myEventId io
reactOnEvent, reactOnEventTimer ::
SndSeq.AllowInput mode =>
Int -> WX.Window a -> Sequencer mode ->
(Event.T -> IO ()) ->
IO ()
reactOnEvent _interval frame (Sequencer h _) action = do
mvar <- MVar.newEmptyMVar
void $ forkIO $ forever $ do
MVar.putMVar mvar =<< Event.input h
WXCore.evtHandlerAddPendingEvent frame =<< createMyEvent
registerMyEvent frame $
MVar.takeMVar mvar >>= action
-- naive implementation using a timer, requires Non-Blocking sequencer mode
reactOnEventTimer interval frame sequ action =
void $
WX.timer frame [
WX.interval := interval,
on command := getWaitingEvents sequ >>= mapM_ action]代码显示了两种处理该问题的方法:
reactOnEventTimer使用WX timer.reactOnEvent执行忙碌等待,只有当事件实际到达时才会激活。这是首选的解决方案。在我的示例中,我等待ALSA MIDI sequencer消息。Event.input调用等待下一条ALSA消息到来。action获取Event.input的结果,也就是传入的ALSA消息,但它在WX线程中运行。
https://stackoverflow.com/questions/3176682
复制相似问题