Michael Mossey wrote:
Duncan
Coutts wrote:
On Wed, 2009-09-09 at 18:29 -0700, Michael P
Mossey wrote:
I'm trying to learn qtHaskell. I realize
few people on this list know anything about qtHaskell, but I have a
question that probably relates to all GUIs as implemented in Haskell. I
just need a hint that could help me figure out the next step, which I
might be able to infer from the qtHaskell API.
Ultimately it's done by some kind of mutable state, either an IORef,
MVar or a thread.
In wxHaskell, the 'simplest' way to code this looks something like the
following (literate Haskell)
Structure
containing 'state' of all of the GUI objects
> data UIState = UIState { uiConnect :: Button ()
> , uiPort :: TextCtrl ()
> , uiUser :: TextCtrl ()
> , uiPasswd :: TextCtrl ()
> , uiSandbox :: TextCtrl ()
> , uiClients :: ComboBox ()
> , uiChanges :: SingleListBox ()
> , uiChangeInfo :: TextCtrl ()
> , uiOrigin :: TextCtrl ()
> , uiUpdate :: TextCtrl ()
> , uiFrame :: Frame ()
> }
> uiState = unsafePerformIO $ newMVar (Nothing :: Maybe UIState)
Ensure that we initialize exactly once...
> uiInitState bt pt us pw sb cl ci ch or up f =
> takeMVar uiState >>= \st ->
> case st of
> Nothing -> let st' = UIState bt pt us pw sb cl ci ch or
up f in
> putMVar uiState (Just st')
> Just _ -> return ()
Get the mutable state.
Note that in the error case we deliberately do not put the MVar back,
as a means
to block all threads waiting on the MVar (as this would indicate a
general
programming/threading issue to be identified).
> getMVarState mv txt =
> takeMVar mv >>= \may_st ->
> case may_st of
> Nothing -> error (txt ++ " is not available")
> Just st -> putMVar mv may_st >>
> return st
Fetch the UI state - this will fail fatally if we fetch before state is
initialized
> uiGetState = getMVarState uiState "UI state"
I don't have anything as neat to show you as Duncan's suggetion (I'd
also be interested to see a cleaner way to do it - this sort of code
always grates a little with me, although all of the major Haskell GUI
bindings seem to need a similar programming style.
However, at the most basic 'trying it out' level, I suspect that
something very like this will work just as well for qtHaskell as it
does for wxHaskell.
On top of these you can layer nicer stuff
like a state monad (with a
'runState' function that saves and restores from an IORef).
A personal favourite of mine is having the GUI event handler post data
over a channel to a thread. That thread reads from the channel and
deals
with the events. The state of the GUI app is then held as local
parameters in that thread.
Doing this of course requires that the GUI lib you're using can cope
with normal Haskell (forkIO) threads. This is possible with gtk2hs, I
don't know about the others.
Regards
Jeremy