1—
Introduction to Perl/Tk
There are many different modules available that extend the functionality of Perl. This book will concentrate on the Tk module. The Tk module allows us to easily add a graphical interface to our Perl scripts and still use all the features that make Perl great. Instead of requiring a typed command with some options or user input on the command line, your program is invoked with an icon or a simple command and the interface handles everything from there.
The Tk extension to Perl doesn't come with the standard distribution of Perl.* You'll need to get it separately from a CPAN site and install it. After you install the Tk module, you simply add use Tk; to the top of your Perl scripts.
A Bit of History About Perl (and Tk)
Originally, Perl was written as a "quick-fix" to a problem Larry Wall was having with his job. Typical of all self-admittedly lazy people, he found a better and easier way to do it, and thus Perl was born. It has since evolved into a widespread and well-used language. Perl has been made available for numerous different platforms, has been well documented, and best of all, is license-fee free. Hopefully the reason you're looking at this book is because you're already converted to the way-of-Perl and want to know how to utilize it to the fullest.
The Tk extension to Perl handles all the widgets, whodads, and whatsits that combine to make a graphical interface. It was ported by Nick Ing-Simmons from Tcl/Tk for use with Perl. A common misconception is that you need Tcl/Tk installed in addition to Perl and Tk for the whole thing to work, but all you really need is Perl
* Unless you get the Win32 binary from CPAN, or another pre-built distribution such as ActiveState Perl.
and its Tk extension. Thanks to a lot of work by a lot of other people, self-admittedly lazy people like me can download the binary for the machine they have and install it in less than 10 minutes (download time not included). You can also compile it from source for your machine.
Perl/Tk for Both Unix and Windows 95/NT
As I was writing this book and cursing the fact that I didn't have enough machines at home to write this book with MS Word and test code examples on my Linux machine without booting back and forth from OS to OS, a miracle was happening. The Tk extension for Perl was ported to Windows. By Windows, I mean the overly-large, overly-influencing OS that is the default on most PCs nowadays, Microsoft Windows. Most people don't have C compilers for their Windows machines, but thanks to the work of Gurasamy Sarathy, there's a great binary distribution of Perl and a good selection of the Perl extensions, including Tk. You simply download the binary, run the install, and you're ready to go.
There are no differences between the way you write Perl/Tk applications on a Unix machine and the way you write them on a Windows machine. You can use any simple text editor on either system. There is a small difference in the way you run them; see Appendix B, Operating System Differences, for details. For now, I'll just say that I prefer to run my Perl applications on Windows NT 4.0 (Service Pack 3) rather than Windows 95.
Versions
When I started writing this book, the latest and greatest versions of Perl and Tk were 5.003 and 400.202. Since then, a Win32 version of the Tk module has been developed and released. Perl has also had some changes. Right before the book was going to print, the port of Tk800.007 was in beta and Perl was up to 5.004_68 (also beta). I have made every effort to include information that is relevant to the new version of Tk and Perl and to test the examples with the new versions. There are certain instances (fonts, for example) where some significant functionality has been added in the new version of Tk. I have tried to note all the changes where they apply, but for the most part, you don't have to worry about which version you have.
Why Use a Graphical Interface?
Hopefully, you bought this book (or are considering it in the bookstore) because you have some idea of why you might want to use a graphical interface for one or more of your scripts. Just in case you don't, read on....
Because you are familiar with writing Perl scripts, you understand the ways you can get information in and out of one. It usually involves a combination of reading/writing files, command-line options, and possibly, data in or out at application runtime (STDIN/STDOUT, using pipes (|) or <>. Certain applications can run with no input, and others, such as an installation script, require constant information fed to it from the user: Do you want to install this file? Can I overwrite this DLL? Do you want to create this directory? Do you want the help files? Sometimes you can set up a bunch of defaults so the user just has to press return to say yes, but then they are stuck sitting at the keyboard and waiting for the next question to come up. Wouldn't it be nice to gather all that information up front and then have the user press a Go button to execute all the steps after the decisions were made?
A GUI interface adds a little flair and professionalism to an application. However, there are times when it would be overkill to add a GUI to a script. If all you are doing is reading in one file, munging a bit with no user input, and spitting out another, a GUI would be silly and unnecessary. GUI interfaces work best where you require a lot of decisions and input from the user, such as our installation example in the preceding paragraph.
Here are some examples of good uses for a graphical user interface:
• A mini web client that connects to a dictionary server and lets you look up words.
• An application that takes a regular expression as input and displays the state map graphically in a scrollable window.
• An application that interfaces with a database and displays query results in several widgets, with labels to indicate what the data is.
• A mail reader that interfaces with your inbox and can also send out mail messages.
• Sometimes your boss says "make it easy to use!" and that usually means either a wrapper around a script or an interface that makes it easy for users to understand the decisions they have to make. Your users also might be used to a graphical interface rather than a command-line interface.
Why Use Perl/Tk?
Have you ever tried to draw a window using so-called "native" facilities? If you do it in C, you'll end up with about 100 lines of code just to create a Hello World program, whether you use MS Windows or X Windows. This doesn't even include an Exit button that would allow you to quit the application nicely.
I have used several different methods to draw windows and create GUI applications throughout my programming life. Using the basic X Windows routines (such
as X_Create_Line_from_x_to_y) is basically a drag. True, you have total control over every little detail, but then again, you have to control every little detail. Sometimes I like not knowing exactly how the button got drawn; it is enough for me to know that it did. (I drive a car, and don't exactly understand the intricate details of the combustion engine. I like that I can turn the key and succeed in my mission of driving to work.)
You have probably seen several books on Tcl/Tk. The problem with Tcl is that you have to program within the constraints of the Tcl language. I much prefer using a language that I already know really well and adding on to it.
Perl/Tk provides you with all the annoying little details. It handles the event loop. It handles drawing the 3D edges on your buttons (if you're not quite sure what I'm talking about, hang in there; I'll explain it all in due time). You can simply use the Perl language to "place a button here," which translates to real Perl as:
$mw->Button(-text => "Something")->pack();
In addition, because of the wonderful community Perl has, there are multitudes of different complex widget types available for use. If you can't find the perfect widget (such as a multi-listbox-selection-thingy with associated canvas), it is fairly simple to create your own by using a combination of some basic and/or not so basic widgets and constructs.
From a programmer's view the bottom line is that using Perl/Tk to write a GUI is fun! It is the best instant-gratification programming high. With just a few lines of code, you can instantly display a button and several other widgets that look like a full-blown application. Of course, it takes a bit more time to code the guts behind it, but it's almost as much fun.
As you go through this book, the best way to understand what is going on is to try lots of different examples. There are tons of working code snippets included for this very reason. Start with the basic Hello World program and change the options to the button as you go through Chapters 2 and 3. See what the results are on your very own screen.
Also, you might want to check out these tools (which we don't cover in this book, but they are fun to use): tkpsh and ptksh (new in Tk800.007, the latest version of Tk for Perl). You can download them from http://www.monmouth.com/~beller. Both programs allow you to type in code on STDIN and have it evaluate each statement (similar to wish).
Installing the Tk Module
Before we go into more details on using Perl/Tk, we should cover how to install it. There are many different ways to get Perl and Tk and install them on your machine. You can get the source and compile it (easy in Unix; not so easy in Ms Windows), or you can get a binary distribution and install that. Some of the binary distributions may not have all the components you want in it though, so make sure you read any README files included with the package.
The two major binary distributions for Perl on Win32 are available from ActiveState (http://www.activestate.com) and CPAN (http://www.perl.com). The binary distribution on CPAN includes the Tk module, so that's the one I'll cover here.
First you need to get Perl installed:
You can test to see if you already have the Tk module installed by using this command (both Unix and Win32):
perl -e 'use Tk'
If you don't get an error, you're ready to go. If you do get one, the error will look like this:
Can't locate Tk.pm in @INC (@INC contains: C:\PERL\lib\site C:\PERL\lib c:\perl\lib c:\perl\lib\site c:\
perl\lib\site .) at myscript line 1.
You'll need to find the Tk module on a CPAN site. Try starting with http://www.perl.com/CPAN/modules/by-module/Tk/. From that directory, find the following files: Tk*readme and Tk*tar.gz (always try to grab the latest versions; the * is for the version number). Be careful when you download the .gz file because some systems try to rename the file to .tar.tar. Simply rename the file back so that it has a .tar.gz extension and it will unzip properly. Follow the instructions in the README file to make sure that you have the right version of Perl already. After downloading Tk*tar.gz, you need to uncompress it using WinZip for MS Windows or gunzip and tar -xvf for Unix. Follow the instructions in the Install file once you have it unpacked. It is very similar to installing Perl itself.
Run the test
perl -e 'use Tk'
again to make sure it all worked correctly. (Note: Windows users will need to use perl -e "use Tk"). For both MS Windows and Unix, make sure your perl/bin directory is in your PATH environment variable. You can then use the widget demo to see what types of widgets are available.
Creating Widgets
All widgets are created in the same basic fashion, with only a few exceptions. Each widget must have a parent widget to watch over it as it is created and keep track of it while it exists in the application. When you create an application, you'll have a central window that will contain other widgets. Usually, that window will be the parent of all the widgets inside it and any other windows you create in your application. You are creating an order to the widgets so the communication between child and parent widgets can happen automatically without any intervention from you once you set it all up.
Assuming that the $parent widget already exists, the generic usage when you create widget Widgettype is as follows:
$child = $parent->Widgettype( [ -option => value, ...] );
Note that the variables that store the widgets are scalars. Actually, they are references to widget objects, but you don't need to know that right now. If you aren't familiar with the object-oriented programming in Perl, using the -> between the $parent and Widgettype invokes the method Widgettype, from the $parent object. It makes the $parent related to the child $child. As you might guess, the $parent becomes the parent of the widget being created. A parent can have many children, but a child can only have one parent. That's pretty much all there is to assigning children to their parents.
When you invoke the Widgettype method, there are usually configuration parameters that you send to set up the widget and interactions within the application. The configuration parameters will occur in pairs: an option and associated value. You will see options similar to -text, -state, or -variable. Notice that the options all start with a dash. Even with the dash, they are really just strings that are labels to indicate the next value to come in the list. Usually, it is not necessary to put quotation marks around the options because Perl is smart enough to recognize them as strings. However, if you are using the -w switch, Perl may complain about an option that it thinks is not text. You can stick quotes around all your options all the time to avoid this, but it shouldn't be necessary. The option names are always all lowercase (except in a few very rare cases, which are noted as we cover them).
Options are specified in list form like this:
(-option => value, -option => value, -option => value)
Don't be fooled by the funny-looking =>; it is just a different way of saying "comma." In fact, you could use just the commas and not the => notation, that is:
(-option, value, -option, value, -option, value)
However, it's much harder to tell which are the option/value pairs. Consider the following syntactically equal statements (which each create a button widget that is 10 pixels by 10 pixels, displays the word "Exit," and performs the action of quitting the application when pressed):
$bttn = $parent->Button(-text, "Exit", -command, sub { exit }, -width, 10,
-height, 10);

$bttn = $parent->Button(-text => "Exit", -command => sub { exit }, -width =>
10, -height => 10);
In the second line, it is much more obvious which arguments are paired together. The option must be directly before the value associated with it: -text is paired with "Exit," -command has the value sub { exit }, and -width and -height both have values of 10.
Congratulations, we're not even done with the first chapter yet, and you already know how to read a typical line of Perl/Tk code!
Quick Definitions of Toplevel, MainWindow, and Frame Widgets
The next chapter covers geometry management, and several of the examples use widgets you don't know anything about yet. Most of the widgets are easy to figure out, but a few require a short introduction.
A MainWindow widget is a special version of a toplevel widget. Both MainWindow and toplevel are the windows that contains other widgets. The only difference between a toplevel and a MainWindow is that the MainWindow is the first window you create in your application. Both of these widgets are covered in greater detail in a later chapter (Chapter 13, Toplevel Widgets).
The other type of widget you need to know about is a frame widget. A frame is a container that can also contain other widgets. It is usually invisible and just used to arrange the widgets as desired. The frame widget is also discussed in its own chapter (Chapter 12, Frames).
Here is what each widget's creation code looks like:
$mw = new MainWindow; # or $mw = MainWindow->new();
$top = $mw->Toplevel();
$frame = $mw->Frame(-borderwidth => 2, -relief => "groove");
For now, just keep in mind the general meanings of MainWindow, toplevel, and frame widgets.
Coding Style
The code lines in a Perl/Tk script can get quite cumbersome and clunky because of all the option/value pairs used in defining and configuring each widget. There are several ways to format the code to deal with readability (and in some cases, ''edit-ability"). Most just involve adding extra spaces or tabs to line up different portions of code. Once you get used to seeing the code, it won't appear to be quite so mysterious and unwieldy.
One coding style places each option/value pair on separate lines (this is my personal favorite, and I use it all the time):
$bttn = $parent->Button(-text => "my text",
                        -command => sub { exit },
                        -width => 10,
                        -height => 10);
With this type of coding style, it is extremely obvious what the pairs are and what value is associated with which option. (You could also go to the extreme of aligning each => to make nice columns, depending on how much time you have to press the space bar.) Some people like to start the option/value pairs on the next line and put the ending ) ; on its own separate line, after the last option/value pair, which retains the comma for formatting ease:
$bttn = $parent->Button(
   -text => "Exit",
   -command => sub { exit },
   -width => 10,
   -height => 10,
  );
This makes the code easier to edit; an option/value pair can be added or deleted on each line without having to mess with parentheses, semicolons or commas. It also keeps the next lines closer to the left side of the page so if you have several indentation levels, you don't end up with code quite so deep to the right.
Sometimes, if there are only one or two option/value pairs, it just makes sense to leave them all on the same line and conserve a little bit of space:
$bttn = $parent->Button(-text => "my text", -command => sub { exit });
Eventually you'll come up with a style that works for the way you read the code and the way you edit it. Whichever way you choose, just try to be consistent throughout your scripts in case someone else takes over the maintenance of your code (it could even be you a year or more down the road).
Displaying a Widget
You use two separate commands to create a widget and display it, although sometimes they are squished into the same line, which makes them look like the same command. In the examples so far, we've used the Button method to create the button, but nothing is displayed by using that method alone. Instead you have to use a geometry manager to cause the widget to be displayed in its parent widget or in another widget. The most commonly used geometry manager is pack, and to use it, you simply call the pack() method on the widget object like this:
$widget->pack();
For example:
$button->pack();
There are arguments that can be sent to the pack method, but we'll cover those in Chapter 2, Geometry Management.
It is not necessary to invoke the pack method on a separate line. The ->pack can be added to the creation of the widget:
$parent->Button(-text => "Bye!", -command => sub { exit })->pack();
The other geometry managers available are grid and place. All three behave differently, and which one you use often depends on the look you are trying to get in your application. Again, look for information on the geometry managers in Chapter 2.
The Anatomy of an Event Loop
When you are programming an application that uses a graphical interface rather than a textual interface, there are a lot of different things to consider. In a text-based application, you can read from standard input (STDIN), use command-line options, read files, or prompt the user for specific information. The keyboard is your only avenue of input from the user. In a GUI, input can not only come from those places, but it can also come from the mouse and the window manager (such as a "close" directive from a window manager like mwm or MS Windows). Although this extra input allows more flexibility in our applications, it also makes our job more difficult. As long as we tell it what to do, Perl/Tk helps us handle all that extra input gracefully.
Input in a GUI is defined by events. Events are typically different combinations of using the keyboard and mouse at the same or different times. If the user pushes the left mouse button on button "B", that is one type of event. Pushing the right mouse button on button "C" would be another event. Typing the letter "a" would
be another event. Yet another event would be holding down the Control key and clicking with the middle mouse button. You get the idea.
Events are processed during an event loop. This event loop does just what its name says-it handles events during a loop. It determines what subroutines to call based on what type of event happened. Here is a pseudo-code event loop:
while (1) {
  get_event_info

  if event is left-mouse-click call process_left_mouse_click
  else if event is right-mouse-click call process_right_mouse_click
  else if event is keyboard-input call type_it
  else handle events for redrawing, resizing etc
}
Obviously, this is a very simplistic approach to an event loop, yet it still shows the basic idea. The event loop is a weeding-out process to determine what type of input was given to the application. For example, the subroutine process_left_mouse_click might determine where the pointer was when the mouse click occurred and then call other subroutines based on that information.
In Perl/Tk, the event loop is initiated by calling a routine called MainLoop. Anything prior to this statement is just setting up the interface. Any code after this call will not happen until after the GUI has been exited by using $mw->destroy.*
If we forget to include the MainLoop statement, the program will think about things for a while and then go right back to the command prompt. None of the windows, buttons, or widgets will be drawn at all. One of the first things that occurs after calling MainLoop is that the interface is drawn and the event loop is started.
Before we get too much further into the event loop and what it does (and what you need to do so it works right), let's look at a real, live, working program, Hello World. (You were expecting something else?)
Hello World Example
Every programming language goes through the Hello World example. It is a good example because it shows how to do something very simple but useful. In our
* Throughout the book, I will use $mw to indicate the variable that refers to the main window created at the beginning of the application.
Hello World example, we'll have the title of our window say "Hello World" and create a button that will dismiss the application:
#!/usr/bin/perl
use Tk;
my $mw = MainWindow->new;
$mw->title("Hello World");
$mw->Button(-text => "Done", -command => sub { exit })->pack;
MainLoop;
Despite only being six lines long, there is quite a bit going on in our little program. The first line, as any Perl programmer knows, invokes Perl (only on Unix; in Win32 you have to type perl hello.pl to invoke the program). The second line tells Perl that we would like to use the Tk module.
The third line
my $mw = MainWindow->new;
is how we create a window. The window will have the same basic window manager decorations as all your other windows. In a Unix environment, it will look like all your other windows, and if it were in MS Windows, it would look like those windows.
The title of our window is changed by using the title method. If we hadn't used this method, the text across the top of the window would be the same as the name of the file containing the code. For instance, if my code were stored in a file named hello_world, the string "Hello_world" would appear across the title bar of my application (Tk automatically capitalizes the first character for you). Using the title method is not required, but it makes the application look more polished.
Any string we put as an argument becomes the title. If I wanted the title to be "Hey! Look at my great program!" this would be the place. This is akin to using the -title option when starting up any standard X Windows application. There are more methods for a MainWindow object, which will be covered later in Chapters 12 and 13.
The next line creates a Button widget, sets basic properties, and packs it. (See Chapter 3, The Basic Button, for all available configuration options.)
The button is set up to display the text "Done" and to perform the Perl command exit when pushed. Finally, the last item of concern is the MainLoop command. This starts the event handler in motion, and from then on the application will do only what we have told it to do: If the user clicks on the button, the application will exit. Anything else the user does-minimizing, resizing, changing to other applications-will all be processed by the window manager and ignored by our application. See Figure 1-1 for a picture of the Hello World window.
0012-01.gif
Figure 1-1.
Hello World window
Using exit Versus Using destroy
In all of the examples in this book you will see sub { exit; } used to quit the Perl/Tk application. This works fine as long as you have done a use Tk; in the same file that contains the sub { exit }. Perl/Tk defines its own exit routine which does some cleanup and various other things that are important to Tk. Another way to quit the Tk portion of the application is to call $mw->destroy(), which destroys the main window and returns to the code listed after MainLoop. The code after MainLoop will not be executed even if you use sub { exit }. Keep this in mind if you are going to be doing anything after the GUI portion is done.
Naming Conventions for Widget Types
Naming conventions? How boring! Well, sometimes our programs get so large and unwieldy that we can't remember what that stupid $button variable was pointing to. If there are over 10 buttons in our program, we would be hard-pressed to figure out which button was $button3 without digging through a bunch of code.
I'm merely going to suggest a naming convention, and if you like it, please use it! If not, either come up with your own, or hope you have a really good memory.
For buttons, I like to use _b, _bttn, or Button as a type of qualifier to the variable name. For instance, I would name my button in the Hello World example $done_b, $done_bttn, or $doneButton.
A specialized widget type is the very first window we create with the MainWindow method. I always use $mw as the variable name for this. You will see other programs use $main or $mainwindow as well.
Table 1-1 contains a list of widget types and my suggested naming conventions for them. Replace "blah" with a sensible description of the widget's purpose (e.g., exit). If you use this convention, you'll always know what type of widget you're working with.
Table 1-1. Naming conventions by widget type
Widget TypeSuggested NameExamples
Button$blah_b (or $blah_bttn, $blahButton)$exit_b, $apply_b, $newButton
Canvas$blah_canvas $blahCanvas$main_canvas, $tinyCanvas
Checkbutton$blah_cb or $blahCheckbutton$uppercase_cb, $lowercaseCheckbutton
Entry$blah_e or $blahEntry$name_e, $addressEntry
Frame$blah_f or $blahFrame$main_f, $left_f, $canvasFrame
Label$blah_l or $blahLabel$name_l, $addressLabel
Listbox$blah_lb or $blahListbox$teams_lb, $teamsListbox
Menu$blah_m or $blahMenu$file_m, $edit_m$helpMenu
Radiobutton$blah_rb or $blahRadiobutton$blue_rb, $grey_rb, $redRadiobutton
Scale$blah_scale or $blahScale$age_scale, $incomeScale
Scrollbar$blah_scroll (or $blah_sbar) or $blahScroll$x_scroll, $yScroll
Text$blah_t (or $blahText)$file_txt, $commentText
Toplevel$blah_w or $blahWindow$main_w, $fileopenWindow

I admit I don't follow my own rules all the time. Throughout this book, you'll see me use just $button in example code. I'll use $button1 and $button2 if there are two in the example. Anything larger than just a few lines, I will try (scout's honor?) to use my own convention. I will always use a name that indicates what type of widget I'm referring to.
Using print for Diagnostic/Debugging Purposes
Normally, you'll run your Perl/Tk program by typing the program name at the command prompt:
% hello_world
or
C:\>perl hello_world
When you invoke the program this way, any output created by using a print (or printf) is to that terminal window. Sometimes, you won't see the information actually printed until you quit the program. This is probably because you didn't put a \n on the end of the string to be printed, which causes an automatic flushing of output. During your application processing, if you think you aren't seeing a print statement when you should be, make sure a \n is on the print statement.
Designing Your Windows (A Short Lecture)
Before you decide what events to handle, it is worthwhile to spend some time sketching out a few windows on paper and deciding what should happen (from the user's perspective) when you click a button or invoke a menu item.
One of the most important things to keep in mind when you design your application's windows is that nothing happens until that event loop starts up. Everything prior to the call to MainLoop is just preparation for the event loop.
A GUI often makes the application look much more polished and purposeful than a command-line interface does. Also, it is often much easier to manipulate many different kinds of user input through a GUI.
Here are some things to consider when you are deciding what the GUI should look like:
• Every widget should have a purpose. It should be intuitive and informative.
• Think about the way a user will use an application and design it accordingly.
• Don't try to cram everything your application is doing into one window.
• Don't always try to separate everything into different windows. Sometimes the application is so simple that one window is all you need.
• Colors are great, but there are a lot of color-blind people out there. If you insist on using color, allow it to be customized via a file or through the application itself.
• Some widgets do their job better than others do. Use the right widget for the right job.
That's it for the lecture. Now, get ready to learn the ropes.