This is my non-toy Gtk2Hs exercise. I will explain more in the future.

User manual: The GUI presents an input text box, an output text box,
and control buttons. If you input text (may be multi-line) into the input
box and press “enter”, the text is given to a String->String
function, and its return value is shown in the output box. If the function
aborts, the error text is shown instead. If the function takes
too long, you may press “break” to stop it. Close the window to quit the
program. Limitation: if the return value is an infinite string, no output
is shown, and you have to press “break” to stop.
Concepts: text boxes, buttons, event handling, various layouts, full evaluation, exception handling, multi-threading, STM
Build environment at the time of writing: GHC 6.12.3, mtl-1.1.0.2, stm-2.1.2.1, gtk-0.12.0
The main program is as simple as this. Remember to link with -threaded.
Bool f n = and [n `mod` d /= 0 | d <- [2..n-1]] -- or generally any time-consuming partial function ]]>
The GeeInteract module:
(String -> String) -> IO ()
interact1 title f = do
initGUI
-- essential elements
inpbuf <- textBufferNew Nothing
inp <- textViewNewWithBuffer inpbuf
outbuf <- textBufferNew Nothing
out <- textViewNewWithBuffer outbuf
inpbut <- buttonNewWithLabel "enter"
brkbut <- buttonNewWithLabel "break"
brkbut `set` [widgetSensitive := False]
clrbut <- buttonNewWithLabel "clear"
clobut <- buttonNewWithLabel "clear"
abortbox <- newEmptyTMVarIO
jobbox <- newEmptyTMVarIO
inpbut `on` buttonActivated $ do
inpbut `set` [widgetSensitive := False]
brkbut `set` [widgetSensitive := True]
x <- get inpbuf textBufferText
textBufferSetText outbuf ""
atomically (putTMVar jobbox (f x))
brkbut `on` buttonActivated $ do
atomically (putTMVar abortbox ())
clrbut `on` buttonActivated $ do
textBufferSetText inpbuf ""
widgetGrabFocus inp
clobut `on` buttonActivated $ do
textBufferSetText outbuf ""
widgetGrabFocus inp
let enable_input = do inpbut `set` [widgetSensitive := True]
brkbut `set` [widgetSensitive := False]
widgetGrabFocus inp
supervisor_thread <- forkIO $ forever $ do
fx <- atomically (takeTMVar jobbox)
dropbox <- newEmptyTMVarIO
work_thread <- forkIO (compute fx (atomically . putTMVar dropbox))
c <- atomically ((takeTMVar abortbox >> return Nothing) `orElse`
(Just `fmap` takeTMVar dropbox))
case c of
Nothing -> do
killThread work_thread
postGUIAsync enable_input
Just y -> postGUIAsync $ do
textBufferSetText outbuf (either show id y)
enable_input
-- placement elements
inpframe <- text_and_but inp [clrbut, inpbut, brkbut] "input"
outframe <- text_and_but out [clobut] "output"
pane <- vPanedNew
panedPack1 pane inpframe True True
panedPack2 pane outframe True True
top <- windowNew
set top [windowTitle:=title,
windowDefaultWidth:=640, windowDefaultHeight:=480]
top `on` deleteEvent $ liftIO (mainQuit >> return False)
containerAdd top pane
widgetGrabFocus inp
widgetShowAll top
mainGUI
text_and_but text buts title = do
scroll <- scrolledWindowNew Nothing Nothing
containerAdd scroll text
row <- hBoxNew False 2
boxSetHomogeneous row False
boxPackStart row scroll PackGrow 0
butsbox <- vButtonBoxNew
set butsbox [boxSpacing := 2, buttonBoxLayoutStyle := ButtonboxStart]
forM_ buts (containerAdd butsbox)
boxPackStart row butsbox PackNatural 0
frame <- frameNew
set frame [frameLabel := title, frameShadowType := ShadowIn]
containerAdd frame row
return frame
compute fx reply = try_all (Ex.evaluate (seq_all fx)) >>= reply
try_all :: IO a -> IO (Either Ex.SomeException a)
try_all = Ex.try
seq_all xs = go xs `seq` xs where
go [] = ()
go (x:xs) = x `seq` go xs
]]>
I have more Haskell Notes and Examples