An event in Gtk2Hs is something that is sent to a widget, by the main loop, usually as a result of an action performed by the user. The widget then responds by emitting a signal, and this is the 'signal' to the program to 'do something'. To the Gtk2Hs application programmer, however, an event is just a Haskell data type with named fields. Many of those are described in the Graphics.UI.Gtk.Gdk.Events section in the API documentation. Look, for example, at the widget signal:
onButtonPress :: WidgetClass w => w -> (Event -> IO Bool) -> IO (ConnectId w)
This is not to be confused with the signal emitted when a 
    Button type widget has been pressed; the button
    here is a mouse button and the signal is emitted when a mouse
    button has been pressed when the mouse is over that widget. The
    handler is a function which takes an event, which has to have
    the 
    Button constructor, and has a IO boolean value. The
    API lists the following fields for 
    Button :
eventSent :: Bool eventClick :: Click eventTime :: TimeStamp eventModifier :: [Modifier] eventButton :: MouseButton eventXRoot, eventYRoot :: Double
The first is used for the return. It occurs in all 
    Event constructors like 
    Motion, Expose, Key, Crossing, Focus, Configure, Scroll,
    WindowState and Proximity. From 
    Events you can extract all kinds of information
    about what the user is doing. A simple example is this code
    snippet:
onButtonPress eb 
                 (\x -> if (eventButton x) == LeftButton 
                           then do widgetSetSensitivity eb False 
                                   return (eventSent x)
                           else return (eventSent x))
    Here parameter 
    eb is the widget covered by the mouse, and the
    anonymous function is of the type as described above. Something
    is done (see the example below) if the left mouse button has
    been pressed and then 
    eventSent returns the appropriate boolean. If
    another mouse button has been pressed, nothing happens, and
    only the boolean is returned.
Now, some widgets don't have associated windows, so they
    just draw on their parents. Because of this, they cannot
    receive events and if they are incorrectly sized, they don't
    clip so you can get messy overwriting (but we won't discuss
    this further). An 
    EventBox provides an X window for its child widget.
    It is a subclass of 
    Bin which also has its own window and which is a
    subclass of 
    ContainerClass .
To create a new EventBox widget, use:
eventBoxNew :: IO EventBox
To add a child we can just use the well known:
containerAdd :: (ContainerClass self, WidgetClass widget) => self -> widget -> IO ()
The window may be visible or invisible, and the event box may be above or below its child in the widget tree. This is determined by:
eventBoxVisibleWindow :: Attr EventBox Bool -- default True eventBoxAboveChild :: Attr EventBox Bool -- default False
If you just want to trap events, then set the window to be
    invisible. If the 
    eventBox is above its child, all events will go to
    it first. If it is below, windows in child widgets of the child
    will be reached first.
A Button Box is just a box which can be used to pack buttons in a standard way. There are two kinds, horizontal and vertical ones, and you construct them with:
hbuttonBoxNew :: IO HButtonBox vButtonBoxNew :: IO VButtonBox
The functionality is in the 
    ButtonBoxClass. 
buttonBoxSetLayout :: ButtonBoxClass self => self -> ButtonBoxStyle -> IO ()
The style is one of the following: 
    ButtonBoxDefaultStyle, ButtonBoxSpread, ButtonBoxEdge,
    ButtonBoxStart, ButtonBoxEnd . You don't pack buttons as
    in ordinary horizontal and vertical boxes, but you use the 
    containerAdd function instead.
The second feature of button boxes is that you can define one or more of your buttons to be in a secondary group. These will then be treated differently when the button box is resized. For example, a help button can be kept visually apart from the others. The function is:
buttonBoxSetChildSecondary :: (ButtonBoxClass self, WidgetClass child) => self -> child -> Bool -> IO ()
This illustrates the use of event boxes and button boxes:
      
    
The buttons are packed into a vertical button box, with the play button as a secondary child. This is also a mnemonic button, with Alt-P as the accellerator key. The images are placed into event boxes with visible windows, and their background color is set to a shade of green with:
widgetModifyBg eb StateNormal (Color 0 35000 0)
As mentioned in Chapter 5.3 the 
    StateType can be 
    StateNormal, StateActive, StatePrelight, StateSelected or
    StateInsensitive .
Note that the images above are not all the same size. This does not matter, but some care has to be taken to make the main window large enough. Otherwise borders will disappear when the pictures are switched.
When the user clicks the left mouse button when the mouse is over an event box, it will be set to insensitive with:
widgetSetSensitivity :: WidgetClass self => self -> Bool -> IO ()
This changes the 
    StateType to 
    StateInsensitive and the widget will no longer
    respond to any user events. Furthermore, its appearance changes.
    In the example we've also set the background color to a shade
    of grey.
      
    
We've used tooltips to tell the user the images can be
    frozen. As mentioned in Chapter 4.4 they don't always work in
    GHCi but they do in the compiled version. To flip the images
    randomly, we've used function RandomRIO, as in the previous
    chapter. You may wonder why a tuple of 
    EventBox and 
    Image has been used, instead of just getting the 
    Image from the 
    containerChild attribute of the event boxes. This
    is because it is a write only attribute, it can be 
    set but not retrieved with 
    get .
Finally, if the images are not available in your source code directory, or if you want to expand the slot machine with more slots, there is an ample supply of Brazilian fish at Peixes. They have been classified into salt water (água salgado) and fresh water (água doce) fish for your convenience.
import Graphics.UI.Gtk
import System.Random (randomRIO)
main :: IO ()
main= do
     initGUI
     window <- windowNew
     set window [windowTitle := "Slot Machine",
                 containerBorderWidth := 10,
                 windowDefaultWidth := 350, 
                 windowDefaultHeight := 400]                 
     hb1 <- hBoxNew False 0
     containerAdd window hb1
     vb1 <- vBoxNew False 0
     boxPackStart hb1 vb1 PackGrow 0
     vbb <- vButtonBoxNew
     boxPackStart hb1 vbb PackGrow 0
     resetb <- buttonNewWithLabel "Reset"
     containerAdd vbb resetb
     quitb <- buttonNewWithLabel "Quit"
     containerAdd vbb quitb
     playb <- buttonNewWithMnemonic "_Play"
     containerAdd vbb playb
     set vbb [buttonBoxLayoutStyle := ButtonboxStart, 
              (buttonBoxChildSecondary playb) := True ]
     let picfiles = ["./jacunda.gif", "./pacu.gif", "./tucunaream.gif"]
     evimls <- sequence (map (initEvent vb1) picfiles)
     tips <- tooltipsNew
     sequence_ $ map ((myTooltip tips) . fst) evimls
     onClicked playb (play evimls picfiles)
     onClicked resetb $ sequence_ (zipWith reSet evimls picfiles)
     onClicked quitb (widgetDestroy window)
     widgetShowAll window
     onDestroy window mainQuit
     mainGUI
initEvent :: VBox -> FilePath -> IO (EventBox, Image)
initEvent vb picfile = do
              eb <- eventBoxNew
              boxPackStart vb eb PackGrow 0
              slot <- imageNewFromFile picfile
              set eb[containerChild := slot, containerBorderWidth := 10 ]
              widgetModifyBg eb StateNormal (Color 0 35000 0)
              widgetModifyBg eb StateInsensitive (Color 50000 50000 50000)
              onButtonPress eb 
                 (\x -> if (eventButton x) == LeftButton 
                           then do widgetSetSensitivity eb False 
                                   return (eventSent x)
                           else return (eventSent x))
              return (eb, slot)
reSet :: (EventBox, Image) -> FilePath -> IO ()
reSet (eb, im) pf = do widgetSetSensitivity eb True                 
                       imageSetFromFile im pf  
play :: [(EventBox, Image)] -> [FilePath] -> IO ()
play eilist fplist = 
   do let n = length fplist
      rands <- sequence $ replicate n (randomRIO (0::Int,(n-1)))
      sequence_ (zipWith display eilist rands) where
                     display (eb, im) rn = do
                                  state <- widgetGetState eb
                                  if state == StateInsensitive 
                                     then return ()
                                     else imageSetFromFile im (fplist !! rn)   
myTooltip :: Tooltips -> EventBox -> IO ()
myTooltip ttp eb = tooltipsSetTip ttp eb "Click Left Mouse Button to Freeze" ""