Thursday, December 7, 2006

Include GUIs in your server programming with Perl/Tk

Modern GUI toolkits make for easy system-level GUIs

As a system programmer, you habitually work with command-line interfaces. Perhaps you've lost track of how easy it can be to wrap existing functionality with a lightweight graphical user interface (GUI). New Perl/Tk releases make it timely to remember that sometimes high quality accompanies ease of use.

You can keep your focus on highly productive server-side programming, and still choose to jazz up your interfaces occasionally. What's more, lightweight toolkits such as Perl/Tk make it possible to do this without the costs of higher-profile GUI approaches.

Toolkit landscape

What are the costs? GUIs are generally harder to program, test, port, and deploy than their corresponding command-line-oriented relatives. Microsoft Visual Basic (VB) is widely recognized for its simplification of GUI construction. However, effective isolation to the Windows world and its clumsiness as a programming language constrain VB; see Resources later in this article for details on these disadvantages. Although Java, GTK+, and Qt have become accepted for Linux and related open source development, it takes a lot of study and lines of code to begin to use them; their "barriers to entry" discourage low-key transitions from character-oriented to WIMP (windows-icons-menus-pointers) programming.

As simplistic as the Web's model of user interaction is, it suffices for plenty of little jobs. In fact, most of my own GUI programming is of Web applications.

Sometimes, though, the Web's not enough. You need more sophisticated programmability or a different security model. Sometimes you just need a "second source"; a Web-based control panel for your Web server makes your system too fragile. Sometimes the Web's too much: the usual servers and clients have big memory footprints and demand configuration.

There's good news, though. Keep your highly-productive system programming habits. Stir in such lightweight high-level language bindings as Perl/Tk or Tkinter. You'll end up with portable, maintainable, surprisingly well-performing, "GUIfied" versions of the programs you've already developed.

Whether your favorite language is Ruby or Fortran, the right accessories will give you similar opportunities. Here's an example of how it can work for a programmer already comfortable with Perl.

Warming up to Perl/Tk

You know Perl. Its clever succinctness lets you code in five minutes the report generators, network probes, text filters, and process monitors that demand day-long efforts with other languages.

What you might not know, though, is that with just a few lines more, you can turn your Perl applications into well-behaved, good-looking GUI ones. Teodor Zlatanov's article on developerWorks describing cfengine and related Perl utilities (see Resources for a link to the article) shows how to write handy tools such as this tiny script, which shuts down all inetd processes:

Listing 1. Source code for kill_inetd.pl



use Proc::ProcessTable;

$t = new Proc::ProcessTable;

foreach $p (@{$t->table}) {
# note that we will also kill "xinetd" and all processes
# whose command line contains "inetd"
kill 9, $p->pid if $p->cmndline =~ 'inetd';
}


What if you need to shut down several rogue CGI processes? Or what if you want more graceful signaling than the rather brutal "kill 9"? What if your boss or your backup needs to use this script while you're away? The traditional answer is just to fire up an editor, substitute cgi for inetd, and so on, and re-run perl kill_inetd.pl from the command line.

Transform that first listing, though, to Listing 2:

Listing 2. Source code for modified kill_inetd.pl


use Proc::ProcessTable;
use Tk;


sub kill_them {
$t = new Proc::ProcessTable;
foreach $p (@{$t->table}) {
kill $signal, $p->pid if $p->cmndline =~ $keyword;
}
$result_text = "Signal $signal sent to all '$keyword' processes.";
}


$main = MainWindow->new();
$main->Label(-text =>
"Control panel for killing processes\n enter keyword here")->pack();
$main->Entry(-textvariable => \$keyword)->pack();
$radiobutton_frame = $main->Frame->pack();
$radiobutton_frame->Radiobutton(-text => "KILL",
-variable => \$signal,
-value => 9)->pack(-side => left);
$radiobutton_frame->Radiobutton(-text => "TERM",
-variable => \$signal,
-value => 15)->pack(-side => left);
$main->Button(-text => "Send kill signal to named processes",
-command => \&kill_them)->pack();
$main->Label(-textvariable => \$result_text)->pack();

# Set reasonable defaults.
$keyword = "inetd";
$signal = 9;

MainLoop();

Notice, though, what you do not have with this twenty-line script: you don't have pages of declarations and configuration. You don't have any license restriction beyond the familiar Perl ones. You don't need to learn a new language.

New horizons

This is exciting stuff. You can wrap up all your existing Perl-coded applications with skinny GUI scripts to make them into similarly interactive and "user-friendly" programs. In fact, you can easily rewrite utilities you have now so that they can continue to operate in a normal "batch" mode when given appropriate command-line arguments, but they can branch and construct a GUI window otherwise:

Listing 3. Select between command-line and GUI operation


sub batch_operation {
}

sub start_GUI {
use Tk;
...
MainLoop();
}

if (0 == scalar(@ARGV)) {
start_GUI();
}
if (errors_detected_in_@ARGV()) {
start_GUI();
}
batch_operation();


Remember: it's just a few extra lines of the Perl (or Python, or ...) code you already know to give these results. What's more, because Perl acts as "glue" to external applications, these same techniques work to wrap legacy Fortran or C applications that you don't want to touch. Just wrap them up in a bit of Perl with backticks or exec, and you're in business.

Perl/Tk scales well, too. Substantial Perl/Tk applications are in everyday use around the world, including a biochemical simulator, geographic mapping programs, a flight management system, and much more. It's difficult to convey the feel of large-scale Perl/Tk programming. The examples above demonstrate how straightforward it is to "GUIfy" with a few extra lines of Perl. For a fuller sense of Perl/Tk programming "in the large," follow the references in Resources below, and study for yourself the source code of more complex programs.

One program I haven't written yet, except in a crude prototype, is a graphical version of Zlatanov's genetic algorithm modeler (read about it in his developerWorks article listed in Resources). I leave a more satisfying version as an exercise to the reader. My own experiments suggested it'll make a showcase for the approach this column recommends: its batch-mode operation will really come alive when visualized on the convenient Perl/Tk Canvas widget.

If you tried Perl/Tk a few years ago, and were unimpressed, it's time to give it another chance. The Perl/Tk processor has improved a great deal lately. It's far easier for Perl/Tk beginners to install binaries for popular platforms, Windows portability is much better, and the latest releases simply exhibit higher quality. Perhaps even more important, more books on Perl/Tk are available. July's second edition of O'Reilly's Perl in a Nutshell includes a valuable Perl/Tk reference. Best of all, O'Reilly kicked off 2002 with publication of Mastering Perl/Tk by Nancy Walsh and Stephen Lidie. If you plan to work for more than a couple of hours with Perk/Tk, Mastering Perl/Tk is the one reference you need to buy.
Conclusion

GUI work can seem mysterious to systems programmers and administrators. It looks like a specialized pursuit that's rather removed from the kinds of development we normally do in "Server clinic."

It doesn't have to be that way, though. Modern GUI toolkits like Perl/Tk offer good performance and functionality, along with the productivity and ease-of-learning this column most often emphasizes. Take an hour or two this month and give Perl/Tk a chance to help you wrap up your programs more attractively.