I am using WxHaskell to graphically show the state of a program that advertises state updates using TCP (which I decode using Data.Binary). When an update is received, I want to update the display. So I want the GUI to update its display asynchronously. I know that processExecAsync
runs a command line process asynchronously, but I don't think this is what I want.

- 1,581
- 2
- 18
- 31
-
Can you clarify your question. What exactly are you seeking? A model for notifying a Haskell process from a separate process? – Don Stewart Jul 05 '10 at 01:09
-
Here is an example. In a separate process, there is a counter. Every time that the counter is incremented, it sends a message via TCP to other Haskell processes (the clients). The clients manage a gui (in WxHaskell) that displays the value of the counter. When an update is received by the client, I want to update the counter on the display. – Alex Jul 05 '10 at 08:42
-
Based on your comment I have posted an answer. What concepts, if any, in my answer relate to your question? Haskell threads (forkIO)? Communication between threads (MVars, STM/TVars)? Something else or not in my answer? – Thomas M. DuBuisson Jul 05 '10 at 18:45
3 Answers
This is rough code using transactional variables (i.e. software transactional memory). You could use an IORef, MVar, or numerous other constructs.
main = do
recvFunc <- initNetwork
cntTV <- newTVarIO 0
forkIO $ threadA recvFunc cntTV
runGUI cntTV 0
Above you start the program, initialize the network and a shared variable cntTV
threadA recvCntFromNetwork cntTVar = forever $ do
cnt <- recvCntFromNetwork
atomically (writeTVar cntTVar cnt)
threadA
receives data from the network and writes the new value of the counter to the shared variable.
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
reads the shared variable and if there is a change will update the GUI counter. FYI, the runGUI thread won't wake up on retry
until cntTVar
is modified, so this isn't a CPU hogging polling loop.
In this code I've assumed you have functions named updateGUICounter
, initGUI
, and initNetwork
. I advise you use Hoogle to find the location of any other functions you don't already know and learn a little about each module.

- 64,245
- 7
- 109
- 166
-
Thanks for your answer. This is sort of what I had in mind. Unfortunately, we need to call the wxHaskell function `start` which starts the event loop. If we run `start runGUI` then the event loop will never be started. And if we `forkIO` the STM stuff then the GUI is not updated (at least when I tried). – Alex Jul 05 '10 at 20:48
I have come up with a kind of hack that seems to work. Namely, use an event timer to check an update queue:
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
So a client listens for the updates on the TCP connection, adds them to a queue. Every 10ms, an event is raised whose action is to check this queue and show the latest update in a static text widget.
If you have a better solution, please let me know!

- 1,581
- 2
- 18
- 31
-
From what I can tell there isn't any reason gui can't retry on the TVar and do this async (but yes, wx breaks seemingly any time the thread blocks). I also tried inverting the concept and writing `\str -> set st [text := str]` into the TVar then having `startClient` call this to update the GUI, but that seems to block indefinitely on `set`. WX has proven rather frustrating so I think I'll stick with GTK as a result. – Thomas M. DuBuisson Jul 06 '10 at 18:53
I found a solution without a busy wait at: http://snipplr.com/view/17538/
However you might choose a higher eventId in order to avoid conflicts with existing ids.
Here is some code from my module 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]
The code shows two ways to handle the problem:
reactOnEventTimer
does a busy wait using the WX timer.reactOnEvent
only gets active when an event actually arrives. This is the prefered solution.
In my example I wait for ALSA MIDI sequencer messages. The Event.input call waits for the next ALSA message to come. The action
gets the results of Event.input, that is, the incoming ALSA messages, but it is run in the WX thread.

- 577
- 6
- 16
-
1Welcome to StackOverflow. Whilst this may answer the question, [it would be preferable](http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers/8259#8259) to include the essential parts of the answer here, and provide the link for reference. – Chris Sep 26 '12 at 07:33