Developer Forums | About Us | Site Map


Useful Lists

Web Host
site hosted by netplex

Online Manuals

Using advanced widgets in Perl/Tk
By Philipp K. Janert - 2004-10-14 Page:  1 2 3

Create rich user interfaces in Perl

Perl is one of the most popular languages out there, and is used for everything from mission-critical projects to Web applications to "glue." It is not, however, often used for GUI programming and prototyping. Philipp K. Janert thinks it should be, and you probably will too -- after this look at some of the more complex widgets available for Perl/Tk.

In contrast to the widespread use of the Perl language, Perl's GUI toolset, Perl/Tk, is much less popular. This is strange, because it is arguably one of the easiest GUI toolkits to program (at least for UNIX platforms), and therefore suggests itself for user interface prototyping or for quickly providing a user-friendly wrapper around cryptic command-line tools.

One reason for this relative indifference toward Perl/Tk appears to be the perception that it is not very powerful and does not lend itself to sophisticated applications. However, a number of widgets that provide more complex functionality are available as user contributions on CPAN. In this article, I will take a look at some of them and show how they can be used to create a richer user experience. I will also point out some more generally useful techniques for programming with Perl/Tk.

(The other problem with Perl/Tk, of course is, that its widgets look ugly and are not "theme-able." Unfortunately, I cannot do anything about that.)

Prerequisites and availability

A good knowledge of Perl and at least rudimentary experience with Perl/Tk as well as general GUI programming concepts -- events, widgets, callbacks, geometry managers -- are assumed throughout. You can find introductory resources on Perl/Tk in the Resources section of this article.

You can freely download all the widgets discussed in this article from CPAN, the Comprehensive Perl Archive Network. Most have many more options than can be explained here: the perldoc for each widget and -- in some cases -- the source code, provide the ultimate and complete reference.

Tabbed frames: Tk::NoteBook

A common GUI widget, the Tab helps to group a large number of options into smaller subsets and so add structure to, for instance, complicated dialog boxes.

A widget with Tab-like appearance and semantics for Perl/Tk is Tk::NoteBook. An example, using three tabs, is shown in Figure 1. Note that the third tab is disabled.

Figure 1. The NoteBook widget
Figure 1. The NoteBook widget

This example was produced by the following code:

Listing 1: Using the NoteBook widget

use Tk;
use Tk::NoteBook;

$mw = MainWindow->new();
$mw->geometry( "400x100" );
$book = $mw->NoteBook()->pack( -fill=>'both', -expand=>1 );

$tab1 = $book->add( "Sheet 1", -label=>"Start", -createcmd=>\&getStartTime );
$tab2 = $book->add( "Sheet 2", -label=>"Continue", -raisecmd=>\&getCurrentTime );
$tab3 = $book->add( "Sheet 3", -label=>"End", -state=>'disabled' );

$tab1->Label( -textvariable=>\$starttime )->pack( expand=>1 );
$tab2->Label( -textvariable=>\$raisetime )->pack( expand=>1 );
$tab3->Button( -text=>'Quit', -command=>sub{ exit; } )->pack( expand=>1 );


sub getStartTime {
  $starttime = "Started at " . localtime;

sub getCurrentTime {
  $raisetime = " Last raised at " . localtime;
  $book->pageconfigure( "Sheet 3", -state=>'normal' );

Specifying arguments to callbacks
We encountered two different ways to specify parameters to a callback -- either as a flat list:

-command=>[ \&fct, $slide, $bar1, $bar2 ]

or as a direct invocation of the callback from within an anonymous subroutine:

-command=>sub{ convert( $input, $label ) }

The difference between the two methods has to do with variable scoping; more precisely, with the exact time the parameters are evaluated.

In the first case, the parameters are evaluated at the time that the constructor containing the -command attribute is executed. If the values of the parameters change later, this will not be visible inside the callback. This is fine in the ProgressBar example, because the parameters are references to the widgets in the application, which, once constructed, are not subject to change anymore.

In contrast, the contents of the anonymous subroutine used in the second case are not evaluated until this subroutine is actually invoked. In other words, the callback sees the values of the parameters at the time the callback is executed. This is important in the DateEntry example, because the $input variable contains the most recent user input, and therefore changes frequently throughout the program.

Finally, because the body of the convert function was too large to fit conveniently inside the inline sub{ } declaration, we kept convert as a separate function, and merely called it from inside the anonymous subroutine.

As in any Perl/Tk application, we first specify the modules we are going to use and then create a MainWindow. Note that Tk::NoteBook, as well as the other contributed widgets on CPAN, are not part of the standard Tk distribution, and therefore need to be specified explicitly.

We create a NoteBook widget as child to the MainWindow and then add three tabs. The first argument to the add() function is a symbolic name, by which the generated page can be referred to in the notebook: we will make use of it below.

I should make two comments on geometry management when using the NoteBook widget. First, although the tabs are widgets themselves, they do not need to be packed. Their geometry management is handled by the enclosing NoteBook. Second, if the parent window of the NoteBook widget is resizable, it is important to specify both the -fill and the -expand attributes when packing the NoteBook. The latter will make sure that the NoteBook's allocation rectangle will always expand to fill the available space, while the former ensures that the actual NoteBook widget will expand to fill its allocation rectangle.

The tabs accept a variety of attributes. Here we demonstrate -createcmd, -raisecmd, and -state. The first two can be used to register callbacks, which will be invoked when the tab is first created and whenever it is raised, respectively; while the last one can take on the values normal and disabled. We also use the -raisecmd callback on the second tab to switch the third tab from its disabled to its active state once the second tab has been raised for the first time. We do this using the pageconfigure() function on the enclosing NoteBook widget, passing the symbolic name of the referenced tab as the first argument.

Versatile graphical display: Tk::ProgressBar

The Tk::ProgressBar is a widget that displays a graphical representation of a scalar value. Progress bars are commonly used to provide feedback to the user when downloading large files or performing similar long-running tasks. The corresponding widget in Perl/Tk offers some special features that can make it attractive for other uses as well.

Figure 2. The ProgressBar widget
Figure 2. The ProgressBar widget

This example contains two ProgressBar widgets, coupled to a standard Scale. Moving the slider changes the length of the displayed color bar by invoking the callback named fct, which calls the value function on the ProgressBars to set the new length. A value of 100 corresponds to the full length of the visible color bar. This value can be changed, as can be the value corresponding to a zero-length color bar, using the -to and -from attributes, respectively.

Listing 2 demonstrates some of the ways the appearance of the color bar can be customized. The bottom ProgressBar is broken into ten blocks, which are separated by from one another by one-pixel gaps. These are the default values; they can be set by explicitly specifying values for the blocks and gap attributes. Some experimentation with the -padx, -pady, -length, and -borderwidth attributes is generally required.

The -colors attribute accepts a reference to an array containing pairs of positions and colors. (Note that the positions must be sorted in ascending order!) The last specified color will be used for the color bar, until the next position is reached, at which point the color changes. So, defining @colors = ( 0, "red", 50, "green" ); yields a color bar that is red on the left side and green on the other. Here, I provide 100 distinct values, spanning the colors of the rainbow for the top ProgressBar. Note that the top ProgressBar changes its length when the window is resized.

Listing 2: Using the ProgressBar widget

use Tk;
use Tk::ProgressBar;

@colors = (  0, '#ff002a',  1, '#ff0014',  2, '#ff000a',  3, '#ff0500',  4, '#ff1000',
	     5, '#ff1b00',  6, '#ff3000',  7, '#ff3b00',  8, '#ff4600',  9, '#ff5100',
	    10, '#ff6100', 11, '#ff7600', 12, '#ff8100', 13, '#ff8c00', 14, '#ff9700',
	    15, '#ffa100', 16, '#ffbc00', 17, '#ffc700', 18, '#ffd200', 19, '#ffdd00',
	    20, '#ffe700', 21, '#fffd00', 22, '#f0ff00', 23, '#e5ff00', 24, '#dbff00',
	    25, '#d0ff00', 26, '#baff00', 27, '#afff00', 28, '#9fff00', 29, '#95ff00',
	    30, '#8aff00', 31, '#74ff00', 32, '#6aff00', 33, '#5fff00', 34, '#54ff00',
	    35, '#44ff00', 36, '#2eff00', 37, '#24ff00', 38, '#19ff00', 39, '#0eff00',
	    40, '#03ff00', 41, '#00ff17', 42, '#00ff21', 43, '#00ff2c', 44, '#00ff37',
	    45, '#00ff42', 46, '#00ff57', 47, '#00ff67', 48, '#00ff72', 49, '#00ff7d',
	    50, '#00ff87', 51, '#00ff9d', 52, '#00ffa8', 53, '#00ffb8', 54, '#00ffc3',
	    55, '#00ffcd', 56, '#00ffe3', 57, '#00ffee', 58, '#00fff8', 59, '#00faff',
	    60, '#00eaff', 61, '#00d4ff', 62, '#00c9ff', 63, '#00bfff', 64, '#00b4ff',
	    65, '#00a9ff', 66, '#008eff', 67, '#0083ff', 68, '#0079ff', 69, '#006eff',
	    70, '#0063ff', 71, '#004eff', 72, '#003eff', 73, '#0033ff', 74, '#0028ff',
	    75, '#001dff', 76, '#0008ff', 77, '#0200ff', 78, '#1200ff', 79, '#1d00ff',
	    80, '#2800ff', 81, '#3d00ff', 82, '#4800ff', 83, '#5300ff', 84, '#5d00ff',
	    85, '#6e00ff', 86, '#8300ff', 87, '#8e00ff', 88, '#9900ff', 89, '#a300ff',
	    90, '#ae00ff', 91, '#c900ff', 92, '#d400ff', 93, '#df00ff', 94, '#e900ff',
	    95, '#f400ff', 96, '#ff00f3', 97, '#ff00e3', 98, '#ff00d9', 99, '#ff00ce' );

$mw = MainWindow->new();
$mw->geometry( '250x150' );
$mw->resizable( 1, 0 );

$bar1 = $mw->ProgressBar( -borderwidth=>2, -blocks=>100, -gap=>0,
			  -length=>106 )->pack( -padx=>5, -pady=>5, -fill=>'x' );

$slide = $mw->Scale( -orient=>'horizontal', -length=>150,
		     -showvalue=>0, -tickinterval=>20 )->pack;

$bar2 = $mw->ProgressBar( -padx=>2, -pady=>2, -borderwidth=>2,
			  -troughcolor=>'#BFEFFF', -colors=>[ 0, '#104E8B' ],
			  -length=>106 )->pack;

$mw->Button( -text=>'Quit', -command=>sub{ exit } )->pack( -padx=>15, -pady=>15,
							   -anchor=>'se' );

$slide->configure( -command=>[ \&fct, $slide, $bar1, $bar2 ] );


sub fct {
  my ( $slide, $bar1, $bar2 ) = @_;
  my $val = $slide->get();
  $bar1->value( 100 - $val );
  $bar2->value( $val );

We specify the callback for the Scale widget not in its constructor, but later, using the configure function. The reason is that we need references to both ProgressBars as arguments to the callback, and at the time the Scale constructor is invoked, they are not all defined yet. Changing the order in which the widget constructors are called is not possible, since this would change the order in which the widgets are packed.

This code also demonstrates one way to specify arguments to a callback: as an anonymous list, with a reference to the callback invoked as the first element, followed by the required parameters. If a callback does not require arguments, we do not need to create the anonymous list, since Perl treats scalar values, evaluated in list context, as single-element arrays. (For more information on lists in Perl, see Learning Perl, 2nd edition, listed in Resources.) We will encounter a different method for sending parameters to a callback below; check the Sidebar for a discussion of the differences between the two methods.

The freedom to choose arbitrary colors for any segment of the color bar makes the ProgressBar interesting for uses as a general display widget: for instance, the colors can be used to indicate whether the displayed variable is within the "normal" parameter range or not. Unfortunately, the widget does not support an -orient attribute; ProgressBars are always oriented horizontally.

View Using advanced widgets in Perl/Tk Discussion

Page:  1 2 3 Next Page: Simplified data entry: Tk::DateEntry and Tk::PathEntry

First published by IBM developerWorks

Copyright 2004-2024 All rights reserved.
Article copyright and all rights retained by the author.