Wxhaskell asynchronous updates

I use WxHaskell to graphically display the status of a program that advertises state updates using TCP (which I decode using Data.Binary). When the update is received, I want to update the display. Therefore, I want the GUI to update its display asynchronously. I know it processExecAsyncstarts the command line process asynchronously, but I don’t think this is what I want.

+3
source share
3 answers

This is crude code using transactional variables (i.e. software transactional memory). You can use IORef, MVar, or many other constructs.

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. FYI, runGUI retry , cntTVar , .

, updateGUICounter, initGUI initNetwork. Hoogle, , , .

+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 , .

, , !

+1

:  http://snipplr.com/view/17538/

eventId, .

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 , . .

In my example, I am waiting for messages from the ALSA MIDI sequencer. The Event.input call waits for the next ALSA message. actionreceives the results of Event.input, that is, incoming ALSA messages, but is executed on the WX thread.

0
source

Source: https://habr.com/ru/post/1753092/


All Articles