wxhaskell асинхронные обновления - PullRequest
3 голосов
/ 05 июля 2010

Я использую WxHaskell для графического отображения состояния программы, которая объявляет об обновлениях состояния с использованием TCP (который я декодирую с использованием Data.Binary). Когда обновление получено, я хочу обновить дисплей. Поэтому я хочу, чтобы графический интерфейс обновлял свой дисплей асинхронно. Я знаю, что processExecAsync запускает процесс командной строки асинхронно, но я не думаю, что это то, что я хочу.

Ответы [ 3 ]

2 голосов
/ 05 июля 2010

Это грубый код, использующий транзакционные переменные (то есть программную транзакционную память).Вы можете использовать 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 читает общую переменную и, если есть изменения, обновляет счетчик графического интерфейса.К вашему сведению, поток runGUI не будет активирован retry до тех пор, пока cntTVar не будет изменен, так что это не цикл опроса ЦП.

В этом коде я предположил, что у вас есть функции с именем updateGUICounter, initGUI и initNetwork.Я советую вам использовать Hoogle, чтобы найти расположение любых других функций, которые вы еще не знаете, и немного узнать о каждом модуле.

1 голос
/ 06 июля 2010

Я придумал что-то вроде взлома, который работает.А именно, используйте таймер событий для проверки очереди обновлений:

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 мс происходит событие, действие которого заключается в проверке этой очереди и отображении последнего обновления в статическом текстовом виджете.

Если у вас есть лучшее решение, пожалуйста, дайте мне знать!

0 голосов
/ 26 сентября 2012

Я нашел решение без ожидания на: 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 становится активным, только когда событие действительно наступает. Это предпочтительное решение.

В моем примере я жду сообщений секвенсора ALSA MIDI. Вызов Event.input ожидает следующего сообщения ALSA. action получает результаты Event.input, то есть входящие сообщения ALSA, но он запускается в потоке WX.

...