3

WxHaskell を使用して、TCP (Data.Binary を使用してデコード) を使用して状態の更新を通知するプログラムの状態をグラフィカルに表示しています。アップデートを受信したら、表示を更新したい。そのため、GUI の表示を非同期で更新する必要があります。コマンドラインプロセスを非同期で実行することは知ってprocessExecAsyncいますが、これは私が望んでいるものではないと思います。

4

3 に答える 3

2

これは、トランザクション変数(つまり、ソフトウェアトランザクショナルメモリ)を使用した大まかなコードです。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共有変数を読み取り、変更がある場合はGUIカウンターを更新します。参考までに、runGUIスレッドは変更されるretryまでウェイクアップしcntTVarないため、これはCPUを占有するポーリングループではありません。

このコードでは、、、、およびという名前updateGUICounterの関数があると想定しています。Hoogleを使用して、まだ知らない他の関数の場所を見つけ、各モジュールについて少し学ぶことをお勧めします。initGUIinitNetwork

于 2010-07-05T18:32:24.147 に答える
1

私はうまくいくように見える一種のハックを思いついた。つまり、イベントタイマーを使用して、更新キューを確認します。

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ミリ秒ごとに、このキューをチェックし、静的テキストウィジェットに最新の更新を表示するアクションを持つイベントが発生します。

より良い解決策があれば、私に知らせてください!

于 2010-07-05T23:03:08.237 に答える
0

http://snipplr.com/view/17538/でビジーウェイトなしで解決策を見つけました

ただし、既存のIDとの競合を回避するために、より高い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]

このコードは、問題を処理する2つの方法を示しています。

  • reactOnEventTimerWXタイマーを使用してビジーウェイトを実行します。
  • reactOnEventイベントが実際に到着したときにのみアクティブになります。これが推奨されるソリューションです。

私の例では、ALSAMIDIシーケンサーメッセージを待ちます。Event.input呼び出しは、次のALSAメッセージが来るのを待ちます。はaction、Event.inputの結果、つまり着信ALSAメッセージを取得しますが、WXスレッドで実行されます。

于 2012-09-26T07:22:46.187 に答える