Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
wxhaskell异步更新_Haskell_Wxhaskell - Fatal编程技术网

wxhaskell异步更新

wxhaskell异步更新,haskell,wxhaskell,Haskell,Wxhaskell,我使用WxHaskell以图形方式显示程序的状态,该程序使用TCP(我使用Data.Binary对其进行解码)播发状态更新。收到更新后,我想更新显示。因此,我希望GUI异步更新其显示。我知道processExecAsync异步运行命令行进程,但我认为这不是我想要的 这是使用事务变量(即软件事务内存)的粗略代码。您可以使用IORef、MVar或许多其他构造 main = do recvFunc <- initNetwork cntTV <- newTVarIO 0

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

这是使用事务变量(即软件事务内存)的粗略代码。您可以使用IORef、MVar或许多其他构造

main = do
    recvFunc <- initNetwork
    cntTV <- newTVarIO 0
    forkIO $ threadA recvFunc cntTV
    runGUI cntTV 0
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 cntTVar currentCnt=do

counter我想出了一种似乎有效的方法。即,使用事件计时器检查更新队列:

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
startClient::IO(TVar[Update])
startClient=/*连接到服务器,
侦听更新并添加到队列*/
gui::TVar[Update]->IO()
gui trdl=do

f我在以下位置找到了一个没有繁忙等待的解决方案:

但是,您可以选择更高的eventId,以避免与现有ID冲突

以下是我的模块中的一些代码:

myEventId::Int myEventId=WXCore.wxID_最高+100 --自定义事件ID,避免与Graphics.UI.WXCore.Types.varTopId冲突 --|自定义事件注册为菜单事件 createMyEvent::IO(WXCore.CommandEvent()) createMyEvent= WXCore.commandEventCreate WXCore.wxEVT_命令菜单_所选myEventId registerMyEvent::WXCore.EvtHandler a->IO()->IO() registerMyEvent win io= WXCore.evtHandlerOnMenuCommand win myEventId io reactOnEvent,reactOnEventTimer:: SndSeq.AllowInput模式=> Int->WX.窗口a->序列器模式-> (Event.T->IO())-> IO() reactOnEvent\u间隔帧(定序器h\u)动作=do mvar>=mapM_uu动作]
代码显示了两种处理问题的方法:

  • reactOnEventTimer
    使用WX定时器进行繁忙等待
  • reactOnEvent
    仅当事件实际到达时才会激活。这是首选的解决方案

在我的示例中,我等待ALSA MIDI sequencer消息。Event.input调用等待下一条ALSA消息。
操作
获取Event.input的结果,即传入的ALSA消息,但它在WX线程中运行。

您能否澄清您的问题。你到底在寻找什么?从单独的进程通知Haskell进程的模型?下面是一个示例。在一个单独的进程中,有一个计数器。每次计数器递增时,它都会通过TCP向其他Haskell进程(客户端)发送一条消息。客户端管理显示计数器值的gui(在WxHaskell中)。当客户端收到更新时,我想更新显示器上的计数器。根据您的评论,我已经发布了一个答案。我的回答中有哪些概念与你的问题有关?Haskell螺纹(forkIO)?线程之间的通信(MVAR、STM/TVAR)?我的回答中是否有其他内容?谢谢你的回答。这就是我的想法。不幸的是,我们需要调用启动事件循环的wxHaskell函数
start
。如果我们运行
start runGUI
,那么事件循环将永远不会启动。如果我们
forkIO
STM的东西,那么GUI就不会更新(至少在我尝试时是这样)。从我所能看出,没有任何理由GUI不能在TVar上重试并异步执行此操作(但是,是的,wx似乎在线程阻塞的任何时候都会中断)。我还尝试将这个概念颠倒过来,将
\str->set st[text:=str]
写入TVar,然后让
startClient
调用它来更新GUI,但这似乎会无限期地阻塞
set
。WX已经被证明是相当令人沮丧的,所以我认为我会坚持GTK作为结果。欢迎来到StackOverflow。虽然这可以回答问题,但请在此处包含答案的基本部分,并提供链接以供参考。
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
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]