UserGuide(3) User Contributed Perl Documentation UserGuide(3)NAME
Perl/Tk - Writing Tk applications in Perl 5
DESCRIPTION
This document is for beginners. It assumes you know some Perl, and
have it and Tk running. If you are not currently reading this document
courtesy of the widget demonstration program, please be sure to run
widget, as it will show you the various widget types supported by Tk
and how to use them. widget should be installed in your default path,
so type widget at a command prompt.
Here are links to other novice tutorials:
http://www.lehigh.edu/~sol0/ptk/tpj1.html
http://www.lehigh.edu/~sol0/ptk/perlmonth01/pm1.html
Mastering Perl/Tk is the definitive book on Perl/Tk:
http://www.oreilly.com/catalog/mastperltk
Some Background
Tk GUI programming is event-driven. (This may already be familiar to
you.) In event-driven programs, the main GUI loop is outside of the
user program and inside the GUI library. This loop - initiated by
calling MainLoop - watches all events of interest and activates the
correct handler procedures to handle these events. Some of these han‐
dler procedures may be user-supplied; others will be part of the
library.
For a programmer, this means that you're not watching what is happen‐
ing; instead, you are requested by the toolkit to perform actions when‐
ever necessary. So, you're not watching for 'raise window / close win‐
dow / redraw window' requests, but you tell the toolkit which routine
will handle such cases, and the toolkit will call the procedures when
required. These procedures are known as callbacks, and some of them you
write yourself.
First Requirements
Perl programs that use Tk need to include "use Tk". A program should
also use "use strict" and the -w switch to ensure the program is work‐
ing without common errors.
Any Perl/Tk application starts by creating the Tk MainWindow. You then
create items inside the MainWindow, and/or create new windows called
Toplevels that also contain child items, before starting the MainLoop,
which is the last logical statment in your program. You can also create
more items and windows while you're running, using callbacks. Items
are only shown on the display after they have been arranged by a geome‐
try manager like pack; more information on this later. MainLoop starts
the GUI and handle all events. That's all there is to it!. A trivial
one-window example is shown below:
#!/usr/bin/perl -w
use Tk;
use strict;
my $mw = MainWindow->new;
$mw->Label(-text => 'Hello, world!')->pack;
$mw->Button(
-text => 'Quit',
-command => sub { exit },
)->pack;
MainLoop;
Please run this example. It shows you two widget types, a Label and a
Button, and how they are packed. When clicked, the <Button> widget
invokes the callback specified by the -command option. Finally, note
the typical Tk style using "-option" => "value" pairs.
Widget creation
Tk windows and widgets are hierarchical, i.e. one window includes one
or more other windows. You create the first Tk window using "MainWin‐
dow->new". This returns a window handle, assigned to $mw in the exam‐
ple above. Keep track of the main handle, commonly called a widget
reference.
You can use any Tk handle to create child widgets within the window (or
widget). This is done by calling the Tk constructor method on the
variable. In the example above, the "Label" method called from $mw
creates a Label widget inside the MainWindow. In the constructor call,
you can specify various options; you can later add or change options
for any widget using the configure method, which takes the same parame‐
ters as the constructor. The one exception to the hierarchical struc‐
ture is the Toplevel constructor, which creates a new outermost window.
After you create any widget (other than the MainWindow or Toplevels,
you must render it by calling pack. (This is not entirely true; more
later). If you do not need to refer to the widget after construction
and packing, call pack off the constructor results, as shown for the
Label and utton in the example above. Note that the result of the com‐
pound call is the result of pack, which is a valid Tk handle.
Windows and widgets are deleted by calling destroy on them; this will
delete and un-draw the widget and all its children.
Standard Tk widgets
Here is an itemize of the standard Tk widget set.
Button
Canvas
Checkbutton
Entry
Frame
Label
Labelframe
Listbox
Menu
Menubutton
Message
Panedwindow
Radiobutton
Scale
Scrollbar
Spinbox
Text
Toplevel
Perl/Tk provides an equal number of new widgets, above and beyond this
core set.
Adjuster
Ballon
BrowseEntry
ColorEditor
DirTree
ErrorDialog
FileSelect
HList
LabEntry
LabFrame
NoteBook
Optionmenu
Pane
ProgressBar
ROText
Table
TextUndo
Tiler
TList
Tree
Variables and callback routines
Most graphical interfaces are used to set up a set of values and condi‐
tions, and then perform the appropriate action. The Tk toolkit is dif‐
ferent from your average text-based prompting or menu driven system in
that you do not collect settings yourself, and decide on an action
based on an input code; instead, you leave these values to your toolkit
and only get them when the action is performed.
So, where a traditional text-based system would look like this:
#!/usr/bin/perl -w
use strict;
print "Please type a font name\n";
my $font = <>; chomp $font;
# Validate font
print "Please type a file name\n";
my $filename = <>; chomp $filename;
# Validate filename
print "Type <1> to fax, <2> to print\n";
my $option = <>; chomp $option;
if ($option eq 1) {
print "Faxing $filename in font $font\n";
} elsif ($option eq 2) {
print "Now sending $filename to printer in font $font\n";
}
The slightly larger example below shows how to do this in Tk. Note the
use of callbacks. Note, also, that Tk handles the values, and the sub‐
routine uses the method get to get at the values. If a user changes
his mind and wants to change the font again, the application never
notices; it's all handled by Tk.
#!/usr/bin/perl -w
use Tk;
use strict;
my $mw = MainWindow->new;
$mw->Label(-text => 'File Name')->pack;
my $filename = $mw->Entry(-width => 20);
$filename->pack;
$mw->Label(-text => 'Font Name')->pack;
my $font = $mw->Entry(-width => 10);
$font->pack;
$mw->Button(
-text => 'Fax',
-command => sub{do_fax($filename, $font)}
)->pack;
$mw->Button(
-text => 'Print',
-command => sub{do_print($filename, $font)}
)->pack;
MainLoop;
sub do_fax {
my ($file, $font) = @_;
my $file_val = $file->get;
my $font_val = $font->get;
print "Now faxing $file_val in font $font_val\n";
}
sub do_print {
my ($file, $font) = @_;
my $file_val = $file->get;
my $font_val = $font->get;
print "Sending file $file_val to printer in font $font_val\n";
}
The packer - grouping with Frame widgets
In the examples above, you must have noticed the pack calls. This is
one of the more complicated parts of Tk. The basic idea is that any
window or widget should be subject to a Tk geometry manager; the packer
is one of the placement managers, and Bgrid> is another.
The actions of the packer are rather simple: when applied to a widget,
the packer positions that widget on the indicated position within the
remaining space in its parent. By default, the position is on top;
this means the next items will be put below. You can also specify the
left, right, or bottom positions. Specify position using -side =>
'right'.
Additional packing parameters specify the behavior of the widget when
there is some space left in the Frame or when the window size is
increased. If widgets should maintain a fixed size, specify nothing;
this is the default. For widgets that you want to fill up the current
horizontal and/or vertical space, specify -fill => 'x', y, or both; for
widgets that should grow, specify -expand => 1. These parameters are
not shown in the example below; see the widget demonstration.
If you want to group some items within a window that have a different
packing order than others, you can include them in a Frame. This is a
do-nothing window type that is meant for packing or filling (and to
play games with borders and colors).
The example below shows the use of pack and Frames:
#!/usr/bin/perl -w
use Tk;
use strict;
# Take top and the bottom - now implicit top is in the middle
my $mw = MainWindow->new;
$mw->title( 'The MainWindow' );
$mw->Label(-text => 'At the top (default)')->pack;
$mw->Label(-text => 'At the bottom')->pack(-side => 'bottom');
$mw->Label(-text => 'The middle remains')->pack;
# Since left and right are taken, bottom will not work...
my $top1 = $mw->Toplevel;
$top1->title( 'Toplevel 1' );
$top1->Label(-text => 'Left')->pack(-side => 'left');
$top1->Label(-text => 'Right')->pack(-side => 'right');
$top1->Label(-text => '?Bottom?')->pack(-side => 'bottom');
# But when you use Frames, things work quite alright
my $top2 = $mw->Toplevel;
$top2->title( 'Toplevel 2' );
my $frame = $top2->Frame;
$frame->pack;
$frame->Label(-text => 'Left2')->pack(-side => 'left');
$frame->Label(-text => 'Right2')->pack(-side => 'right');
$top2->Label(-text => 'Bottom2')->pack(-side => 'bottom');
MainLoop;
More than one window
Most real applications require more than one window. As you just saw,
you can create more outermost windows by using a Toplevel widget. Each
window is independent; destroying a Toplevel window does not affect the
others as long as they are not a child of the closed Toplevel. How‐
ever, exiting the MainWindow will destroy all remaining Toplevel wid‐
gets and end the application. The example below shows a trivial three-
window application:
#!/usr/bin/perl -w
use Tk;
use strict;
my $mw = MainWindow->new;
fill_window($mw, 'Main');
my $top1 = $mw->Toplevel;
fill_window($top1, 'First top-level');
my $top2 = $mw->Toplevel;
fill_window($top2, 'Second top-level');
MainLoop;
sub fill_window {
my ($window, $header) = @_;
$window->Label(-text => $header)->pack;
$window->Button(
-text => 'close',
-command => [$window => 'destroy']
)->pack(-side => 'left');
$window->Button(
-text => 'exit',
-command => [$mw => 'destroy']
)->pack(-side => 'right');
}
More callbacks
So far, all callback routines shown called a user procedure. You can
also have a callback routine call another Tk routine. This is the way
that scroll bars are implemented: scroll-bars can call a Tk item or a
user procedure, whenever their position has changed. The Tk item that
has a scrollbar attached calls the scrollbar when its size or offset
has changed. In this way, the items are linked. You can still ask a
scrollbar's position, or set it by hand - but the defaults will be
taken care of.
The example below shows a listbox with a scroll bar. Moving the
scrollbar moves the listbox. Scanning a listbox (dragging an item with
the left mouse button) moves the scrollbar.
#!/usr/bin/perl -w
use Tk;
use strict;
my $mw = MainWindow->new;
my $box = $mw->Listbox(
-relief => 'sunken',
-height => 5,
-setgrid => 1,
);
my @items = qw(One Two Three Four Five Six Seven
Eight Nine Ten Eleven Twelve);
foreach (@items) {
$box->insert('end', $_);
}
my $scroll = $mw->Scrollbar(-command => ['yview', $box]);
$box->configure(-yscrollcommand => ['set', $scroll]);
$box->pack(-side => 'left', -fill => 'both', -expand => 1);
$scroll->pack(-side => 'right', -fill => 'y');
MainLoop;
Canvases and tags
One of the most powerful widgets in Tk is the Canvas window. In a
<Canvas> window, you can draw simple graphics and include other wid‐
gets. The <Canvas> area may be larger than the visible window, and may
then be scrolled. Any item you draw on the canvas has its own id, and
may optionally have one or more tags. You may refer to any item by its
id, and may refer to any group of items by a common tag; you can move,
delete, or change groups of items using these tags, and you can bind
actions to tags. For a properly designed (often structured) Canvas,
you can specify powerful actions quite simply.
In the example below, actions are bound to circles (single click) and
blue items (double-click); obviously, this can be extended to any tag
or group of tags.
#!/usr/bin/perl -w
use Tk;
use strict;
# Create B<MainWindow> and canvas
my $mw = MainWindow->new;
my $canvas = $mw->Canvas;
$canvas->pack(-expand => 1, -fill => 'both');
# Create various items
create_item($canvas, 1, 1, 'circle', 'blue', 'Jane');
create_item($canvas, 4, 4, 'circle', 'red', 'Peter');
create_item($canvas, 4, 1, 'square', 'blue', 'James');
create_item($canvas, 1, 4, 'square', 'red', 'Patricia');
# Single-clicking with left on a 'circle' item invokes a procedure
$canvas->bind('circle', '<1>' => sub {handle_circle($canvas)});
# Double-clicking with left on a 'blue' item invokes a procedure
$canvas->bind('blue', '<Double-1>' => sub {handle_blue($canvas)});
MainLoop;
# Create an item; use parameters as tags (this is not a default!)
sub create_item {
my ($can, $x, $y, $form, $color, $name) = @_;
my $x2 = $x + 1;
my $y2 = $y + 1;
my $kind;
$kind = 'oval' if ($form eq 'circle');
$kind = 'rectangle' if ($form eq 'square');
$can->create(
($kind, "$x" . 'c', "$y" . 'c',
"$x2" . 'c', "$y2" . 'c'),
-tags => [$form, $color, $name],
-fill => $color);
}
# This gets the real name (not current, blue/red, square/circle)
# Note: you'll want to return a list in realistic situations...
sub get_name {
my ($can) = @_;
my $item = $can->find('withtag', 'current');
my @taglist = $can->gettags($item);
my $name;
foreach (@taglist) {
next if ($_ eq 'current');
next if ($_ eq 'red' or $_ eq 'blue');
next if ($_ eq 'square' or $_ eq 'circle');
$name = $_;
last;
}
return $name;
}
sub handle_circle {
my ($can) = @_;
my $name = get_name($can);
print "Action on circle $name...\n";
}
sub handle_blue {
my ($can) = @_;
my $name = get_name($can);
print "Action on blue item $name...\n";
}
perl v5.8.8 2004-02-28 UserGuide(3)