[Haskell-beginners] Event handling in GTK2hs: managing events and global state

Александър Л. Димитров aleks.dimitrov at googlemail.com
Wed Mar 25 00:24:30 EDT 2009

Hello community,

I'm just diving into my first bigger Haskell problems, and got stuck at a
certain point with gtk2hs. I couldn't find documentation or even small examples
for it, no matter where I looked, so I turn to this list.

My basic problem is that I want a certain action to happen based on the effects
of a side effect; in my case, loading a file into a widget. Together with the
file, I load a data structure, and define an event that will translate clicks on
the widget into some magic on the data structure.

So when I load a file, I do the following:

> loadFile :: GladeXML -> FilePath -> IO ()
> loadFile xml fp = do widget <- xmlGetWidget -- …
> 		     -- do stuff with the widget, yield some 'data'
> 		     connectId <- widget `on` buttonPressEvent $ handler data
(this is a minimal trivial example, to show the crucial point.) So, the handler
step depends on the particular contents of the file. This means, that for every
file I load, I first have to disable the old action, and only then install a new
one, since I generate a new handler every time a file is loaded. Every addition
of a handler gives me a connectId, which is its true name. And, as the gtk2hs
docs inform me, I have to call it by its true name to dispel it.

The gtk2hs docs suggest I 'keep around' the connectId, but herein lies my
problem. The loadFile function can be called from several spots in the
application (and I'd like to keep it that way.) Even if I wrapped loadFile in a
State monad, those entry points to load file (command line, menu, button,
possibly more) have no way of knowing the state of connectId, lest I make it
global (*shudders*) using unsafePerformIO and a Maybe IORef.

Forgive me if I'm missing something obvious, but I have thus far only used
monads for serialized code flows, and not for asynchronous, event-driven

So the other approach would be (and thanks to mmorrow at freenode for the
idea) to wrap my widget component (if I understood correctly) in a new type that
also keeps track of its connectedId (namely, the event it's been connected to)
via an IORef.  This way the state gets 'hidden' in the IO monad, via IORef. I'd
end up with something like

data WW = WW { widget :: SomeWidget
             , connectId :: IORef (Maybe (ConnectId SomeWidget))

and of course no monads to indicate there's something state-changing going on
(Except the IO monad, which is the underlying monad for the whole brouhaha
anyway.) The above call would look more like:

> main = do …
>           widget <- xmlGetWidget …
>           nothingRef <- newIORef Nothing
>           openButton `onClicked` $ loadFile xml fp (WW widget nothingRef)
> loadFile :: GladeXML -> FilePath -> WW -> IO ()
> loadFile xml fp (WW widget cid) = -- do stuff, yield some 'data'
>                    ref <- readIORef cid
>                    case ref of 
>                         (Just i) -> signalDisconnect i
>                         Nothing   -> return ()
> 		     connectId <- widget `on` buttonPressEvent $
> 						handler data widget
>                    writeIORef cid $ Just connectId

This doesn't strike me as a very elegant way of doing it. I'm sure there is some
nice Haskellery that would make it more beautiful, so I'd like to ask this list
what that might be?

Also, a small plea: the gtk2hs documentation is sorely lacking an example of the
usage of "signalDisconnect". I can provide a working minimal example of this solution,
if anyone is interested, so maybe that could serve as a documentation. Though I
still hope that this isn't the best possible way of doing it.

Thanks in advance,
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
Url : http://www.haskell.org/pipermail/beginners/attachments/20090325/fad74f60/attachment.bin

More information about the Beginners mailing list