[Work on GUI basics John Goerzen **20080422073953] { hunk ./en/ch24-gui.xml 130 + + + Event-Driven Programming + + GTK+, like many GUI toolkits, is an + event-driven toolkit. That means that + instead of, say, displaying a dialog box and waiting for the + user to click on a button, we instead tell gtk2hs what function + to call if a certain button is clicked, but don't sit there + waiting for a click in the dialog box. + + + This is different from the model traditionally used for console + programs. When you think about it, though, it almost has to + be. A GUI program could have multiple windows open, and writing + code to sit there waiting for input in the particular + combination of open windows could be a complicated proposition. + + + Event-driven programming compliments Haskell nicely. As we've + discussed over and over in this book, functional languages + thrive on passing around functions. So we'll be passing + functions to gtk2hs that get called when certain events occur. + These are known as callback functions. + + + At the core of a GTK+ program is the main + loop. This is the part of the program that waits for + actions from the user or commands from the program and carries + them out. The GTK+ main loop is handled entirely by GTK+. To + us, it looks like an I/O action that we execute, that doesn't + return until the GUI has been disposed of. + + + Since the main loop is responsible for doing everything from + handling clicks of a mouse to redrawing a window when it has + been uncovered, it must always be available. We can't just run + a long-running task -- such as downloading a podcast episode -- + from within the main loop. This would make the GUI + unresponsive, and actions such as clicking a Cancel button + wouldn't be processed in a timely manner. + + + Therefore, we will be using multithreading to handle these + long-running tasks. More information on multithreading can be + found in . For now, just know that + we will use forkIO to create new threads for + long-running tasks such as downloading podcast feeds and + episodes. For very quick tasks, such as adding a new podcast to + the database, we will not bother with a separate thread since it + will be executed so fast the user will never notice. + + + + + Initializing the GUI + + Our first steps are going to involve initializing the GUI for + our program. For reasons that we'll explain later, + FIXME: add link we're going to have a small + file called PodLocalMain.hs that loads + PodMain and passes to it the path to + podresources.glade, the XML file saved by + Glade that gives the information about our GUI widgets. + + &PodLocalMain.hs:all; + + Now, let's consider PodMain.hs. This file is + the only Haskell source file that we had to modify from the + example in to make it work as a GUI. Let's + start by looking at the start of our new + PodMain.hs file. + + &PodMain.hs:imports; + + This first part of PodMain.hs is similar to + our non-GUI version. We import three additional components, + however. First, we have Graphics.UI.Gtk, + which provides most of the GTK+ functions we will be using. + Both this module and Database.HDBC provide a + function named disconnect. Since we'll be + using the HDBC version, but not the GTK+ version, we don't + import that function from Graphics.UI.Gtk. + Graphics.UI.Gtk.Glade contains functions + needed for loading and working with our Glade file. + + + We also import Control.Concurrent, which has + the basics needed for multi-threaded programming. hunk ./examples/ch24/PodDB.hs 2 --- ch23/PodDB.hs +-- ch24/PodDB.hs addfile ./examples/ch24/PodLocalMain.hs hunk ./examples/ch24/PodLocalMain.hs 1 - +{-- snippet all --} +-- ch24/PodLocalMain.hs +module Main where + +import qualified PodMain + +main = PodMain.main "podresources.glade" +{-- /snippet all --} hunk ./examples/ch24/PodMain.hs 1 -{-- snippet all --} --- ch23/PodMain.hs +{-- snippet imports --} +-- ch24/PodMain.hs hunk ./examples/ch24/PodMain.hs 4 -module Main where +module PodMain where hunk ./examples/ch24/PodMain.hs 22 +{-- /snippet imports --} + +{-- snippet type --} hunk ./examples/ch24/PodMain.hs 41 +{-- /snippet type --} hunk ./examples/ch24/PodMain.hs 43 --- import Paths_Pod(getDataFileName) - -main = withSocketsDo $ handleSqlError $ +main gladepath = withSocketsDo $ handleSqlError $ hunk ./examples/ch24/PodMain.hs 48 - gui <- loadGlade + gui <- loadGlade gladepath hunk ./examples/ch24/PodMain.hs 52 - mainGUI + mainGUI -- Main GTK loop; exits when GUI done hunk ./examples/ch24/PodMain.hs 56 -loadGlade = - do Just xml <- xmlNew "podresources.glade" +loadGlade gladepath = + do Just xml <- xmlNew gladepath hunk ./examples/ch24/PodMain.hs 177 -{-- /snippet all --} + hunk ./examples/ch24/PodParser.hs 2 --- ch23/PodParser.hs +-- ch24/PodParser.hs hunk ./examples/ch24/PodTypes.hs 2 --- ch23/PodTypes.hs +-- ch24/PodTypes.hs }