Written by Steve Lidie
by Steve Lidie, April 1999
A favorite Perl module of mine is the Tk widget toolkit. With this collection of widgets, you can write sophisticated graphical applications, or just jazz-up your old command line program. Oh, you don't know what a widget is? How about a gadget, or a control? Actually, they're all just names that refer to a graphical element, such as this button:
Image Figure 1
You don't often see a naked button - usually it's embedded in a window, as part of a complete program. When you push the button it reacts and does something. The point here is to realize that widgets are just discrete entities that you arrange to make a whole graphical application. Tk has lots of other widgets, like labels, menus, dialogs, scrollbars, listboxes, and so on, as well as geometry managers to arrange them as you desire. Of course, there's more to a graphical application than just presenting a pretty window - it must interact with a life form (humanoid only, currently). As we'll see, things known as events help mediate the application-human conversation.
Tk was developed by John Ousterhout, originally for use with his scripting language, Tcl. That's where I first discovered it, and I found I could write my GUIs considerably faster that ever before. The Tk programs were also one tenth the size, had fewer bugs, and were more robust than an Xlib version.
I was hooked on Tk, but I faced a dilemma, because I was also hooked on Perl! I sometimes found myself writing a program in both Tcl/Tk (for the GUI) and Perl (for the systems stuff) - a sad state of affairs, indeed. Then Nick Ing-Simmons arrived on the scene, and, after a lot of work, molded Tk version 4 into a Perl extension. But Nick didn't stop there, he expanded Perl/Tk by adding new widgets, incorporating Tix and user contributed widgets, and making the package object oriented. Throughout the years Perl/Tk has been updated, tweaked and tuned, and now corresponds to Tcl/Tk version 8.0. The Perl/Tk version I base this article on is Perl 5.005_02 and Tk800.013, both available on the CPAN (for Unix), or http://www.activestate.com/(for Win32).
CPAN stands for Comprehensive Perl Archive Network, 100 or so machines spread around the globe, each a repository of everything that's Perl. I presume you have Perl installed and running somewhere, and, if it's not at least version 5.005_02, you'll update it. I do not presume you have Perl/Tk, so in a nutshell, here's how you install it.
Assuming you're on Unix, use a web browser and go to http://www.perl.com/ and click on the "Nearest CPAN Site" link. Then link to "authors", "Nick Ing-Simmons". Grab the latest tar file, presumably "Tk800.013.tar.gz" and save it somewhere. Like all well behaved Perl modules, the following steps should suffice for installation:
gunzip Tk800.013.tar.gz
tar -xvf Tk800.013.tar
cd Tk800.013
perl Makefile.PL
make
make test
make install
If you do not have the proper permissions to install Tk, you'll have to fetch and install Perl in your own file space before you can install Tk.
For Win32 users, install the latest Perl from the ActiveState site mentioned above. Then, at a DOS prompt, run PPM (the Perl Package Manager program), and install Tk:
ppm
PPM>install Tk
PPM>quit
To verify that Tk was installed properly, run the widget program from a Unix or DOS prompt. This program demonstrates most of the Perl/Tk widgets, and lets you view the code, modify it, and rerun demonstrations with your changes. Writing a GUI is fundamentally different than the classical line mode program - you use a technique called event driven programming. Essentially, it's your job to write subroutines, or callbacks, that are automatically called when certain events occur. Interesting events might be a button press, a file becoming readable, the expiration of a timer, or typing a keyboard character, and it's the subroutine's job to handle the event appropriately. To illustrate the concepts, we'll examine the typical "Hello World" program - actually, I'll show two versions. Here's the first, which produced Figure 2:
Image Figure 2
1 #!/usr/bin/perl -w
2 #
3 # hiworld1 - "Hello World" with a Label and explicit event binding.
4
5 use Tk;
6 use strict;
7
8 my $mw = MainWindow->new;
9
10 my $label = $mw->Label(-text => 'Hi World');
11 $label->pack;
12 $label->bind('' => sub {$mw->destroy});
13
14 MainLoop;
Note lines 1 and 6, where we specify run with warning enabled (-w) and strict programming style (use strict). I highly recommend you too write all your Perl/Tk program this way - it will find subtle (and sometimes stupid!) errors.
Line 3 is a comment, telling us that the program displays a Label widget. Labels (usually) draw simple text strings, in a particular font, size and color. Labels are also the simplest Tk widget - about the only other thing you can do with them is create explicit event bindings. More on this shortly.
Line 5 imports all the Tk widgets, definitions, variables and subroutines.
Line 8 is required by all Perl/Tk programs - it opens the program's first (main) window, the container within which (most) all your other widgets are arranged. This widget arrangement, or layout, is performed by a geometry manager. The geometry manager determines the actual location and size of a widget in the main window, relative to all other widgets. A widget never appears in the main window until it has been managed by a geometry manager.
Line 8 also demonstrates the OO (object oriented) nature of Perl/Tk. Here the scalar variable $mw is initialized with an object reference to a new instance of a MainWindow. We'll learn some basic OO concepts as we go along, but you needn't be an OO guru to use Perl/Tk, as long as you're not building new Perl/Tk widgets -and we aren't.
Line 10 creates our first widget, an instance of a Label, which displays the string "Hi World". (There may be many widgets of class Label in a program, and each may have a different text string, font, font size and color, but any particular one is known as an instance.) This line demonstrates typical Perl/Tk widget creation syntax: an object ($mw) has inherent operations called methods - Label() is one, a constructor method, because is constructs a new instance of a label widget. The methods are really subroutines that may have parameters, specified as keyword/value pairs. All keywords, like -text, start with a dash. Note that we use the => operator as the argument list separator, not to be confused with the -> operator used to invoke a method on an object.
Line 11 tells the pack() geometry manager to manage the label in the main window, which then displays it (Figure 2).
Line 12 is how we exit the program. It defines an anonymous subroutine that sends a "destroy" message to the object $mw, the main window. destroy() is a method which frees all memory associated with a widget and removes it from the display. When $mw is destroyed, everything contained in it is destroyed as well, and the program ends.
Line 12 uses the bind() method to associate an event with a subroutine. Subroutines of this type are termed callbacks, because the code is called back when the event in question (here, pressing Button-1 on the mouse) occurs.
Line 14 kick-starts the program by entering the Tk event loop. The event loop dispatches events to callbacks (yours and Tk's), and runs continuously until the main window goes away. Forget this statement and your Perl/Tk program thinks a while, then exits without displaying anything!
In summary, here are the characteristics of most Perl/Tk programs:
* A use Tk statement which imports the base Tk definitions.
* A MainWindow that contains other application widgets.
* Widget creation commands to populate the main window.
* Binding and callback creation commands.
* Geometry management commands to arrange and display widgets.
* A MainLoop() command to begin program execution.
Now let's look at a better version of the previous "Hello World" program; this new program generated Figure 3 . Compare Figures 2 and 3 and note the visual difference. There area several other subtle differences, as we'll see shortly.
Image Figure 3
1 #!/usr/bin/perl -w
2 #
3 # hiworld - "Hello World" with a Button and implicit class bindings.
4
5 use Tk;
6 use strict;
7
8 my $mw = MainWindow->new;
9
10 my $button = $mw->Button(-text => 'Hi World', -command => \&exit);
11 $button->pack;
12
13 MainLoop;
Line 3 reminds us that the program displays a Button widget rather than a Label, and that the button has "implicit class bindings". In this case, the button has a builtin binding for a event, so we don't have to create it manually as we did for the label. This binding applies to every instance of class Button - it's one feature that differentiates labels from buttons.
A button also has a three dimensional appearance due of its "raised" relief. If you'd like to make a label look like a button, just change its -relief option using the ubiquitous configure() method:
$label->configure(-relief => 'raised');
But adding a relief and a button binding still doesn't turn a label into a button because there are even more class bindings inherited by a button instance. Move the cursor over a button and watch what happens - it becomes highlighted. This is because the button has an binding, which invokes a builtin Tk callback that lightens the button. Similarly, the button has a binding to restore the widget's appearance when the cursor leaves. And finally, there are bindings to change the button's relief to "sunken" when selected, and then back to "raised".
Line 10 creates the button, and now we know some of things that go on behind to scene to make our programming life easier. The -command argument automatically links the code reference \&exit to a event.
It's time for something more complex - how about an application skeleton? By that I mean a main window with a menubar across the top, and File and Help menubuttons, which you can use as a basis for your real applications. Along the way we'll visit the Dialog widget, icons, and see some window manager commands. To make things really easy I've included a tiny Perl program named tkskel that will generate our mini application.
To use tksel, type something like "tkskel frog", and a complete Perl/Tk program named frog is created. Here's the resulting code:
1 #!/usr/bin/perl -w
2 #
3 # frog, Sun Apr 11 18:01:41 1999
4
5 use 5.005;
6 use Tk 8.0;
7 use Tk::widgets qw/Dialog/;
8 use subs qw/build_menubar fini init/;
9 use vars qw/$MW $VERSION/;
10 use strict;
11
12 $MW = MainWindow->new;
13 init;
14 MainLoop;
15
16 sub build_menubar {
17
18 # Create the menubar and File and Quit menubuttons. Note
19 # that the cascade's menu widget is automatically created.
20
21 my $menubar = $MW->Menu;
22 $MW->configure(-menu => $menubar);
23 my $file = $menubar->cascade(-label => '~File');
24 my $help = $menubar->cascade(-label => '~Help', -tearoff => 0);
25
26 # Create the menuitems for each menu. First, the File menu item.
27
28 $file->command(-label => "Quit", -command => \&fini);
29
30 # Finally, the Help menuitems.
31
32 $help->command(-label => 'Version');
33 $help->separator;
34 $help->command(-label => 'About');
35
36 my $ver_dialog = $MW->Dialog(-title => 'frog Version',
37 -text => "frog\n\nVersion $VERSION",
38 -buttons => ['OK'],
39 -bitmap => 'info');
40 my $about_dialog = $MW->Dialog(-title => 'About frog',
41 -text => 'About frog',
42 -buttons => ['OK']);
43 my $menu = $help->cget('-menu');
44 $menu->entryconfigure('Version', -command => [$ver_dialog => 'Show']);
45 $menu->entryconfigure('About', -command => [$about_dialog => 'Show']);
46
47 $menubar; # return the menubar
48
49 } # end build_menubar
50
51 sub fini {
52
53 exit;
54
55 } # end fini
56
57 sub init {
58
59 $VERSION = '1.0';
60
61 $MW->title("frog $VERSION");
62 $MW->iconname('frog');
63 my $xpm = $MW->Photo(-file => "skull.xpm", -format => 'xpm');
64 $MW->Icon(-image => $xpm); # full color icon picture
65
66 my $menubar = build_menubar;
67 my $frame = $MW->Frame(qw/-width 300 -height 50 -background cyan/)->pack;
68
69 } # end init
With the exception of line 7, I suspect lines 1 through 14 are self-explanatory. Line 7 is how we import Tk widgets that are not "core" widgets. Here are the 15 standard widgets automatically imported when we say "use Tk": Button, Canvas, Checkbutton, Entry, Frame, Label, Listbox, Menu, Menubutton, Message, Radiobutton, Scale, Scrollbar, Text, and Toplevel.
Lines 16 through 49 build the menubar, but I'll defer most of that discussion for another time. For now, it remains a task for you, gentle reader, to figure out!
Line 61 is a window manager command - it defines the text shown at the top of the window's title bar.
Some window managers support iconization, and line 62 simply gives our icon a name. Likewise, line 64 changes the default appearance of the icon to a colored image of our own design. To do that we first need to create a Tk image object, which is what line 63 does. The image is a small XPM file of, fittingly, a skull, grabbed from http://www.skellramics.com/ and used with their permission. Here's what frog looks like iconified:
Image Figure 4
Lines 66 and 67 complete the creation of the GUI elements. Line 66 creates the menubar and related components and saves its widget reference in $menubar, just in case we need to (re)configure it later. Line 67 creates a colored frame and packs it after the menubar. I don't cover geometry management here, suffice it to say that by default the packer arranges widgets top down in the main window, like this:
Image Figure 5
Notice that the Help menubutton has been pressed, a menu posted, and the Version menu item highlighted. Pressing Version pops up this modal dialog:
Image Figure 6
Notice that the dialog is independent of our main window - it's built from a special widget called a toplevel, which is similar to a main window. And like a main window, you can pack other widgets inside a toplevel. When we press OK the dialog is withdrawn from the display - but it's not destroyed - dialogs are persistent objects and must be explicitly destroyed.
Obviously, there's a lot more we can talk about, but enough's enough. To help straighten out the tangle I've no doubt made of your mind, take a look at these important Perl/Tk resources (in no particular order):
The man pages! For instance: perldoc Tk::Dialog
My list of important Perl/Tk links: http://www.Lehigh.EDU/~sol0/ptk/ptk.html
Post a Comment