【问题标题】:wxhaskell asynchronous updateswxhaskell 异步更新
【发布时间】:2010-07-05 00:24:45
【问题描述】:

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

【问题讨论】:

  • 你能澄清你的问题吗?你到底在寻找什么?从单独的进程通知 Haskell 进程的模型?
  • 这里是一个例子。在一个单独的过程中,有一个计数器。每次计数器增加时,它都会通过 TCP 向其他 Haskell 进程(客户端)发送一条消息。客户端管理一个显示计数器值的 gui(在 WxHaskell 中)。当客户端收到更新时,我想更新显示屏上的计数器。
  • 根据您的评论,我已经发布了答案。我的回答中的哪些概念(如果有)与您的问题有关? Haskell 线程(forkIO)?线程之间的通信(MVars、STM/TVars)?我的回答中是否还有其他内容?

标签: haskell wxhaskell


【解决方案1】:

这是使用事务变量(即软件事务内存)的粗略代码。您可以使用 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 计数器。仅供参考,在 cntTVar 被修改之前,runGUI 线程不会在 retry 上唤醒,所以这不是 CPU 占用的轮询循环。

在这段代码中,我假设您有名为updateGUICounterinitGUIinitNetwork 的函数。我建议您使用 Hoogle 查找您不知道的任何其他功能的位置,并了解每个模块的一些知识。

【讨论】:

  • 感谢您的回答。这就是我的想法。不幸的是,我们需要调用 wxHaskell 函数start 来启动事件循环。如果我们运行start runGUI,那么事件循环将永远不会启动。如果我们 forkIO STM 的东西,那么 GUI 不会更新(至少在我尝试时)。
【解决方案2】:

我想出了一种似乎可行的技巧。即,使用事件计时器检查更新队列:

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 连接上的更新,并将它们添加到队列中。每 10 毫秒触发一个事件,其作用是检查此队列并在静态文本小部件中显示最新更新。

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

【讨论】:

  • 据我所知,没有任何理由 gui 无法在 TVar 上重试并执行此异步操作(但是,是的,wx 似乎在线程阻塞的任何时候都会中断)。我还尝试反转概念并将\str -&gt; set st [text := str] 写入TVar,然后让startClient 调用它来更新GUI,但这似乎无限期地阻止set。 WX 已被证明相当令人沮丧,所以我想我会坚持使用 GTK。
【解决方案3】:

我找到了一个解决方案,无需忙于等待: http://snipplr.com/view/17538/

但是,您可以选择更高的 eventId 以避免与现有 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线程中的。

【讨论】:

  • 欢迎来到 StackOverflow。虽然这可能会回答问题,但it would be preferable 在此处包含答案的基本部分,并提供链接以供参考。
猜你喜欢
  • 2018-07-24
  • 1970-01-01
  • 1970-01-01
  • 2020-01-31
  • 2019-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多