Linux Format #31 [[ Typographical notes: indented text is program listing text surrounded in _underscores_ is italicised/emphasised ]] Perl tutorial ///TITLE: Perl goes Graphical - part two ///STRAP: In the second of two tutorials about Perl and GUI programming, Charlie Stross gives a guided tour of graphics systems for perl. ///SUBTITLE: To Tk or not to Tk In last month's tutorial we took a whistle-stop tour of the basics of Perl/Tk. Tk, the graphics toolkit invented by Professor John Ousterhout for Tcl, the Tool Command Language, is one of the most portable GUI programming kits. Tk has been ported to MacOS and Windows as well as UNIX (including Linux), and in addition to its native Tcl bindings Tk has been coerced into cooperating with Python, Perl, and Ruby -- its high-level approach and ease of use make it one of the easiest environments in which to build a graphical application. Most of Tk's problems are rooted in its origin as the very first high-level cross platform widget toolkit. It looks like -- and feels like -- the ageing Motif toolkit on UNIX, and it behaves in a similarly old-fashioned way on Windows and MacOS. It isn't themable, or rather, it can't interoperate with other GUI toolkits such as Qt or Gtk at the level of reading their themes; its look and feel is distinctive and may not fit in with newer desktop environments. Drag and drop isn't implemented very well, and cut and paste to other applications -- if set up in a Tk application -- is only at the level supported by the X11 window manager; there's no intelligent clipboard. While it is possible to assemble complex multi-component widgets using Tk -- such as notebook objects or multidoc editor windows -- these aren't provided by the core environment. And it's difficult to do low-level work in Tk; for example, there's no 3D subsystem. None of these objections mean that you shouldn't work with Tk. Its position as the oldest GUI toolkit means that it's also the best-documented, and the one with most support and the largest collection of third-party widgets and tools floating around the net. See [[BOXOUT: "Perl/Tk Resources"]] for examples of information about Perl/Tk that can help you get started. But there are tools you can use to make Perl/Tk easier to use, and alternative toolkits that may fit your need better (if, for example, you need to write an application front-end that absolutely has to look at home under KDE or GNOME). ///SUBTITLE: Making Perl/Tk easier Last month we discussed the basic structure of a Perl/Tk program. The graphical interface is specified as a hierarchy of widgets -- represented by an array of Perl/Tk objects -- and the actions they trigger are written as separate subroutines called 'callbacks'. Because this effectively separates the back-end functionality of an application from its graphical elements, it's eminently practical to design a program in two parts. For example, suppose you're writing a Usenet newsreader or a mail client. You might write a Perl module that provides a class that represents a usenet session (connecting to a news server, selecting groups, selecting articles, listing and displaying headers, displaying articles by number) or mail session (opening a mailbox, listing headers, displaying message bodies, and so on). Additional methods might do things like send a new message, return a list of messages, and so on. All of this functionality is separate from the graphical 'front end', which you can code separately in Perl/Tk, just dropping in callbacks (actually method calls into your back-end module) to make everything work. You might think there's an easier way to design a Perl/Tk application than using graph paper and pencil to grid out the widgets, and then laboriously transcribe them into Perl/Tk commands, and you'd be right. Back when Ousterhout's Tcl team were being funded by Sun (and then a separate spin-off company, Scriptics), they developed a program called SpecTcl -- this was a GUI generator similar to Visual Basic, targeted at Tcl/Tk and Java. An additional bolt-on patch turned it into SpecPerl, able to output Perl/Tk files. SpecTcl fell by the wayside as Scriptics sold its programming assets on, but the tool has been resurrected in the form of SpecTix, available from http://starship.python.net/crew/mike/Spectix/. SpecTix is a multi-language, multi-toolkit GUI generator. It uses the Tix extended mega-widgets for Tk (which provide items like complex file selection dialog boxes, notepads, and so on -- see tix.sourceforge.net). It can spit out code in Perl, Ruby, or Tcl using the Tk toolkit, or in Python with the Tkinter toolkit (designed as a successor to Tk, and not yet supported by Perl), or even Java using the older AWT widget toolkit. The older SpecTcl GUI builder is also still available, from spectcl.sourceforge.net, and Mark Kvale's patches to turn it into SpecPerl (with Perl generation) are available from http://www.keck.ucsf.edu/~kvale/specperl. Neither of these tools will turn you into an experienced GUI programmer. They're both relatively crude when compared to commercial GUI builders, or even more recent Linux toolkits such as Glade. However, used correctly they can take a lot of the donkey work out of building a Perl/Tk user interface that relies on a grid geometry manager. (SpecTcl and descendants insist on using a grid because this allows them to generate UI code that looks roughly the same on whichever graphical system it runs on. The grid is constraint-based, so that objects show up in the same position relative to each other, even if their absolute size varies slightly due to things like font or widget size differences on different platforms.) The one weakness of these tools is their lack of flexibility; if you want to build a placer-based tool such as a drawing application, SpecPerl or SpecTix will let you lay out menus and windows, but you're going to have to do a certain amount of GUI programming by hand. ///SUBTITLE: wxWindows and Perl wxWindows (from www.wxwindows.org) is a free cross-platform GUI application programming toolkit, written in C++. It was started in 1992 at the University of Edinburgh, as part of an academic meta-CASE project that needed to produce programs that could run on Windows and UNIX workstations. Over time, users contributed ports to MacOS and the Xt toolkit (a low-level X11 API), as well as Motif. The current main platform for wxWindows is GTK+, the Gnome toolkit, with the Motif, Windows, MacOS, openVMS and BeOS platforms also supported. Work is under way on building an embedded version of wxWindows. It's distributed under a slightly modified version of the GPL license, with the additional constraint of permitting binary-only distribution of programs created with wxWindows. Because it's a cross-platform system, wxWindows is usually provided as a DLL, and gives wxWindows applications a bunch of utility classes for tasks such as file handling, image handling, and HTML parsing (using the high-level wxHTML library) -- HTML parsing is not up to the standard of, say, the Gecko rendering engine, but is adequate for uses like writing online help systems, and can be extended. There's also extensive internationalization support, debugging tools -- and then a huge range of GUI widgets. wxWindows programs start by instantiating a wxApp object, a program wrapper. They typically then create a frame (a container class), then various other widgets within the frame (such as buttons, menus, scroll bars, and so on). In wxPerl, we start by creating a new class derived from Wx::App. This needs to have a special method called OnInit, which defines the windows used by the application. For windows with different controls (such as scroll bars), you need to define different classes; the idea is you build a wxPerl application by defining classes and then hooking everything together. Here's Jouke Visser's Hello Word program: ///BEGIN CODE LISTING 01: #!/usr/bin/perl -w 02: use strict; 03: use Wx; 04: 05: ########################################################### 06: # 07: # Define our HelloWorld class that extends Wx::App 08: # 09: package HelloWorld; 10: 11: use base qw(Wx::App); # Inherit from Wx::App 12: 13: sub OnInit 14: # Every application has its own OnInit method that will 15: # be called when the constructor is called. 16: { 17: my $self = shift; 18: my $frame = Wx::Frame->new( undef, # Parent window 19: -1, # Window id 20: 'Hello World', # Title 21: [1,1], # position X, Y 22: [200, 150] # size X, Y 23: ); 24: $self->SetTopWindow($frame); # Define the toplevel window 25: $frame->Show(1); # Show the frame 26: } 27: 28: ########################################################### 29: # 30: # The main program 31: # 32: package main; 33: 34: my $wxobj = HelloWorld->new(); # New HelloWorld application 35: $wxobj->MainLoop; ///END CODE LISTING What happens here is that we start by defining a Perl module called HelloWorld. HelloWorld inherits Wx::App, but adds its own OnInit method. the OnInit method creates a new Wx::Frame object, tells the HelloWorld object that the new Wx::Frame is its toplevel window, and then calls the show() method on the frame. Once we've finished with the HelloWorld module, the main() package simply calls the HelloWorld constructor method (new) and then invokes the wxWindows main event loop. To actually put something in the frame, you need to define another class; typically a subclass of Wx::Frame that has some button objects. This is then hooked into the top level class's OnInit method (by having OnInit create an instance of the new class). wxWindows follows a different model for event handling (callbacks) from Tk; events and the action to take when one is received are stored in an event table. For example, to create a menu you create menu entries and associate them with event ID's; you then need to declare that events with the specified event ID's will be handled within objects of the current class by some named method or subroutine. All in all, wxWindows has a neater object-oriented model than Tk, which lends itself to producing large applications with a lower risk of side- effects. It also provides a choice of appearances and greater portability. However, it exposes more of its guts and requires more care when programming -- for example, you can avoid dealing with events in Tk for much longer than you can in wxWindows. If you understand what you're doing it's probably a superior way to produce cross-platform applications, but if you're a novice GUI programmer you will find Perl/Tk easier to get started with. ///SUBTITLE: Qt/KDE and Perl Qt is another cross-platform GUI programming toolkit, written in C++, and distributed under both commercial and free software licenses by Troll Tech of Norway (http://www.troll.no/). Qt is intended as a platform for cross-platform GUI programming; Qt 3.0 supports MacOS, Windows, and UNIX/X11, in addition to Qt/Embedded (for embedded systems). It's the toolkit underlying the KDE desktop, which alone is enough to make it important to Linux users. Qt is highly object-oriented; you create a Qt application by creating a new qApp object, and then adding more objects (each widget has its own class) to the app. Widgets -- and the user -- communicate by using signals and slots -- events such as a mouse-down action send a signal, and one or more widgets can register their interest in such events by using the connect() method to say that they want to be activated when the event occurs. The Qt documentation can be found at: http://doc.trolltech.com/2.0/ -- be warned, it's intimidatingly huge, but doesn't say a word about PerlQt. To find PerlQt, go to http://search.cpan.org/search?dist=PerlQt. There are numerous supporting modules here, although the documentation mostly focusses on explaining how to map the C++ standard conventions into Perl. Important note: PerlQt supports Qt 2.1 and KDE 2, not the most recent Qt 3.0 as yet (although work is in progress on this). PerlQt is a complete API for the Qt toolkit in Perl. It provides packages that wrap around each C++ class provided by Qt; for example, the qApp Qt class is replaced directly by the Qt::App Perl module. (The mapping is direct and name-based; Qt's own classes have a leading lowercase 'q', and you can usually count on the PerlQt equivalent simply dropping the 'q' and prefixing it with Qt::, so that for example a qWidget in C++ becomes a Qt::Widget in Perl. All PerlQt applications begin this way: #!/usr/bin/perl -w use Qt; import Qt::app; We go about creating a new application object like this: my $app = Qt::app->new(); Widgets are created like this: my $button = Qt::PushButton->new("Hello, World"); $button->resize(100,30 We tell the application that $button is its main widget and that it's visible this way: $app->setMainWidget($button); $button->show(); And we make it execute it's main loop (and then exit) like this: exit $app->exec(); There are a number of examples with the PerlQt distribution. However, explicit support for KDE is lacking -- a couple of very early releases of PerlKDE exist and can be found in CPAN, but they're by no means comprehensive and the newest of them dates to 2000 and only supports KDE 1.1.1. You may want to use PerlQt if you're an existing Qt developer, or want to write a Qt application that makes use of Perl's facilities (because Qt apps tend to work well with KDE, sharing a lot of the underpinnings of that environment). However, PerlQt is probably not the first choice for a GUI programming toolkit in Perl, because of the lack of recent releases and the stalled support for KDE, the environment with which it is most closely associated. ///SUBTITLE: Gtk/GNOME and Perl Gtk-Perl is a set of Perl modules that let you write Gtk+ and GNOME applications in Perl. Gtk+, the GIMP toolkit, is a C (not C++) GUI programming system that is the underpinning of the GIMP and, more recently, the GNOME desktop project. It's distributed under the LGPL license, and Gtl-Perl itself is distributed under GPL. The Gtk-Perl home pages are at www.gtkperl.org, and the tutorial can be found at http://personal.riverusers.com/~swilhelm/gtkperl-tutorial/. Despite being written in C, the developers of Gtk+ implemented it using classes and callbacks; the reason for using C was that it made it easy to link C libraries into a variety of other languages, including Perl, Python, Eiffel, Guile, and others. Here's the "Goodbye World" example from the Gtk-Perl tutorial, by way of getting a feel for what a Gtk-Perl program looks like: ///BEGIN CODE LISTING 1:#!/usr/bin/perl -w 2: 3:use Gtk; # load the Gtk-Perl module 4:use strict; # a good idea for all non-trivial Perl scripts 5: 6:set_locale Gtk; # internationalize 7:init Gtk; # initialize Gtk-Perl 8: 9:# convenience variables for true and false 10:my $false = 0; 11:my $true = 1; 12: 13:# widget creation 14:my $window = new Gtk::Window( "toplevel" ); 15:my $button = new Gtk::Button( "Goodbye World" ); 16: 17:# callback registration 18:$window->signal_connect( "delete_event", \&CloseAppWindow ); 19:$button->signal_connect( "clicked", \&CloseAppWindow ); 20: 21:# show button 22:$button->show(); 23: 24:# set window attributes and show it 25:$window->border_width( 15 ); 26:$window->add( $button ); 27:$window->show(); 28: 29:# Gtk event loop 30:main Gtk; 31: 32:# Should never get here 33:exit( 0 ); 34: 35:### Callback function to close the window 36: 37:sub CloseAppWindow 38: { 39: Gtk->exit( 0 ); 40: return $false; 41: } ///END CODE LISTING When you run this program you should see this: ///INCLUDE IMAGE goodbyeworld.jpg Does it look eerily familiar yet? It should be: we start by creating a new window object (Gtk::Window), and a button object (a Gtk::Button). Unlike Tk, but like Qt and wxWindows, we register a connection between named events ("delete_event" for the window object, or "clicked" for the button object) and a callback subroutine (sub CloseAppWindow, right at the bottom). We invoke the show() method on the button, ensuring that it shows up, and we dink with the window a little to specify how wide its border is -- then we tell it that the button belongs to it, and call show() on it, before entering the main event loop. It's deja vu all over again -- and for good reason: almost all GUI toolkits work the same way! We create new objects, where each object corresponds to a visible entity in our user interface, such as a window or a button, or perhaps a container class that holds other entities. We set attributes on these objects (such as size, colour, caption text, and so on), and tell them they belong to each other, in a hierarchy growing down from the top level application widget. We then use some mechanism to tell each object how to handle events the user may send it -- for example, by clicking on a button. Gtk uses the signal_connect method, Qt uses its' signals and slots, Tk does it implicitly by specifying callback arguments (but you can still handle signals directly in Tk if you care to), and wxWindows makes you register events with its event table -- they're all doing the same thing. Finally, you tell the program to execute its main loop. It runs, polling for events and calling whichever subroutines are triggered by them, until something or other yells "exit!" in a crowded widget tree and the action stops. Gtk+ has a number of nice features which are accessible from Perl. A number of widgets don't actually need a toplevel window to contain them -- there's support for floating widgets. You can mess around with the focus policy of your application, rendering widgets sensitive or insensitive to incoming events, and you can force them to adopt your desired size. A number of container classes have associated layout rules associated with them (like Tk's packer and placer) -- the packing box and table widgets. There are also facilities that other toolkits don't have, such as the Gtk ItemFactory -- a class that generates objects belonging to another class on demand (for example, to emit the masses of button objects items required to populate a set of cascading hierarchical menus). Gtk-Perl is worth looking at if you need features beyond those offered by Tk, want an up-to-date look and feel (or interoperability with GNOME), and don't mind too much if your application doesn't run on MacOS, OpenVMS or more obscure systems. It's also worth investigating if you strongly support the GPL. ///SUBTITLE: Glade-Perl All the kits we've looked at so far build applications the old-fashioned way -- by gluing together a tree of widgets using pointers (or in the case of perl, by passing references to objects). As it happens, there _is_ a different way to do things. Mozilla, the web browser project, uses a different paradigm. Using Xptoolkit, the user interface is written in XML, against a strict Document Type Definition (DTD) that corresponds to a hierarchy of widgets. The XUL document is parsed by the application, which uses the description therein to build a tree of widgets. (Remember, XML is a hierarchical, semantic description system that lets you nest arbitrary objects.) Widgets in an XUL file can trigger fragments of JavaScript that hook into the back end of Mozilla or invoke XPCOM objects from elsewhere; in a very real way, XUL delivers on the separation of user interface from back-end functionality we discussed at the start of this tutorial. The bad news is, there's no XUL interpreter and Perl glue available at this time. But the good news is that GNOME has hatched its own equivalent of XUL, in the form of Glade, the Gtk+/GNOME user interface builder. Glade is an application builder; you use it to visually design applications by dragging and linking widgets from a palette. When you tell Glade to save a project, it saves an XML file that describes the user interface you drew along with C source code files that contain stub subroutines for each callback you added to the interface. When you run a Glade program, it links to libglade which interprets the XML specification file and re-draws the widget hierarchy. Glade-Perl is glue that allows you to link libglade to Perl programs, so that you can use Glade to design the user interface for a Gtk or GNOME project rather than assembling the widget hierarchy yourself. Glade also supports direct generation of Perl programs, so that you get a skeleton Perl program that, when executed, will use libglade to generate its user interface -- all you have to do is write the back-end code that does whatever it is that you want to do when you click each button or activate a given widget. You will need to grab GLADE (a graphical application development tool) from http://glade.gnome.org/, and Glade-Perl from http://www.glade.connectfree.co.uk/. You will also have to install a bunch of perl modules that Glade-Perl depends on, notably XML parsers and related stuff, and make sure that you've got Gtk+, libglade, and preferably GNOME installed on your system. Arguably, systems like Glade and XUL show the road forward for GUI programming. By abstracting the user interface from the back-end functionality completely (in a manner that wxWindows strives, and fails, to do, and Tk is a disaster at), they make it easy to redesign the front end of a program or allow the developer to make huge changes behind the scenes while preserving the look and feel. With a user interface design tool like Glade-Perl that lets you spit out the stub of perl that hooks the user interface into the back end, UI design becomes a job for human factors specialists and graphic designers rather than programmers. If you're developing for GNOME, or just want to play with the future, this is the way to go. ///END (Body copy) ///BEGIN BOXOUT: Perl/Tk resources Perl/Tk has been around for ages, and there's a lot of information about it on the web. The port was originally written by Nick Ing-Simmons, and the central website for all things Perl/Tk is http://www.perltk.org/. Cameron Laird currently maintains the FAQ, which is at http://www.perltk.org/contrib/ptkFAQ.html; there are also a large number of contributed modules (mostly implementing additional widgets) on CPAN under Tk, and under http://www.perltk.org/contrib/. There's a public mailing list for Perl/Tk users, and an archive is online at http://faqchest.dynhost.com/prgm/ptk-l/. The first book on Perl/Tk was "Learning Perl/Tk" by Nancy Walsh (O'Reilly and Associates, ISBN 1-56592-314-6). This is now out of print, but is available online from O'Reilly and Associate's subscription-based Safari service (http://safari.oreilly.com/), which provides access to a bookshelf of O'Reilly titles in return for a monthly subscription. "Learning Perl/Tk" is an excellent introductory-level tutorial that covers the basics of programming in Perl/Tk. It's not a Perl tutorial -- you need to have some knowledge of Perl before tackling it -- but provided a thorough grounding in the basics of manipulating widgets, using geometry managers, and the essentials of designing a Perl/Tk application. That book has now been superseded by "Mastering Perl/Tk" by Stephen Lidie and Nancy Walsh (O'Reilly and Associates, ISBN 1-56592-716-8). Lest you think I'm plugging O'Reilly in particular, bear in mind that nobody else is publishing books about Perl/Tk -- and O'Reilly has a specialist unit devoted to writing books about Perl, that can call on the services of Larry Wall (an O'Reilly employee!) to say nothing of most of the other Perl prophets. "Mastering Perl/Tk" is the definitive book on Perl/Tk. In addition to containing the tutorial material of "Learning Perl/Tk", "Mastering Perl/Tk" adds discussion of advanced topics such as handling interprocess communication in Perl/Tk, creating megawidgets, working with images, and developing new widgets for Perl/Tk in C (for performance). It also provides an extensive programmer's reference to the Tk.pm module and its components. At 750-odd pages, it's a bit intimidating -- but it's still easy to get to grips with compared to the tree-slaying bookshelf that is the X11 or Motif manuals. I'd have to rate this book as absolutely indispensible to anyone who is even vaguely thinking about programming in Perl/Tk. Finally, O'Reilly publish the relatively tiny "Perl/Tk Pocket Reference" (ISBN 1-56592-517-3). At 101 pages it's a bit on the fat side for a pocket reference, but that's more to do with the sheer size and number of widgets provided by Perl/Tk. The pocket reference contains a complete run-down of all the methods and graphical elements in Perl/Tk. You won't learn the language from it, but if you're an experienced Perl/Tk programmer and don't fancy carrying around half a forest's worth of paper it'll save your back, as well as your pockets. ///END BOXOUT (Perl/Tk resources)