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