2010-07-05 93 views
3

我正在使用WxHaskell以图形方式显示使用TCP(我使用Data.Binary解码)通告状态更新的程序的状态。收到更新时,我想更新显示。所以我希望GUI以异步方式更新它的显示。我知道processExecAsync异步运行命令行进程,但我不认为这是我想要的。wxhaskell异步更新

+0

你能否澄清你的问题。你究竟在寻找什么?从单独的进程通知Haskell进程的模型? – 2010-07-05 01:09:11

+0

这里是一个例子。在一个单独的过程中,有一个计数器。每次计数器递增时,它都会通过TCP向其他Haskell进程(客户端)发送消息。客户端管理一个显示计数器值的gui(在WxHaskell中)。当客户端收到更新时,我想更新显示器上的计数器。 – Alex 2010-07-05 08:42:37

+0

根据你的评论我发布了一个答案。如果有的话,我的回答中有哪些概念与你的问题有关? Haskell线程(forkIO)?线程之间的通信(MVars,STM/TVars)?别的东西或不在我的答案? – 2010-07-05 18:45:46

回答

2

这是使用事务变量(即软件事务内存)的粗略代码。你可以使用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 cnt 

runGUI读取共享变量,如果有变化将更新GUI计数器。仅供参考,runGUI线程不会在retry上被唤醒,直到cntTVar被修改,所以这不是一个CPU占用轮询循环。

在这段代码中,我假设你有函数updateGUICounter,initGUIinitNetwork。我建议你使用Hoogle来查找你还不知道的其他功能的位置,并学习一些关于每个模块的知识。

+0

感谢您的回答。这是我想到的。不幸的是,我们需要调用启动事件循环的wxHaskell函数'start'。如果我们运行'start runGUI',那么事件循环将永远不会启动。如果我们'forkIO'STM的东西,那么GUI不会更新(至少在我尝试时)。 – Alex 2010-07-05 20:48:27

1

我想出了一种似乎可行的hack。也就是说,使用事件计时器来检查更新队列:

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,就会发起一个事件,其操作是检查此队列并在静态文本小部件中显示最新更新。

如果您有更好的解决方案,请告诉我!

+0

从我可以告诉没有任何理由贵公司不能在TVAR重试,并做这种异步(但是,是的,任何时候线程阻塞wx似乎中断)。我也尝试了反转这个概念,并且将'st str [text:= str]'写入TVar,然后让'startClient'调用这个来更新GUI,但是这似乎无限地阻塞了'set'。 WX已经证明相当令人沮丧,所以我认为我会坚持使用GTK。 – 2010-07-06 18:53:08

0

我发现在没有忙等待一个解决方案: http://snipplr.com/view/17538/

然而,你可能会为了避免与现有的ID冲突选择更高的事件ID。

这里是我的模块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定时器忙等待。
  • reactOnEvent只有在事件实际到达时才会激活。这是首选解决方案。

在我的例子中,我等待ALSA MIDI音序器消息。 Event.input调用等待下一个ALSA消息。 action获取Event.input的结果,即传入的ALSA消息,但它在WX线程中运行。

+1

欢迎来到StackOverflow。虽然这可能回答 问题,[这将是更可取的](http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers/8259 #8259)在这里包括答案的基本部分,并提供供参考的链接。 – Chris 2012-09-26 07:33:06