home *** CD-ROM | disk | FTP | other *** search
- =head1 NAME
-
- Perl/Tk - Writing Tk applications in Perl 5
-
- =for category Introduction
-
- =head1 DESCRIPTION
-
- This document is for beginners. It assumes you know some B<Perl>, and
- have it and Tk running. If you are I<not> currently reading this
- document courtesy of the B<widget> demonstration program, please be
- sure to run B<widget>, as it will show you the various widget types
- supported by Tk and how to use them. B<widget> should be installed in
- your default path, so type I<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
-
- I<Mastering Perl/Tk> is the definitive book on Perl/Tk:
-
- http://www.oreilly.com/catalog/mastperltk
-
- =head1 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 B<MainLoop> - watches all events of interest and activates
- the correct handler procedures to handle these events. Some of these
- handler procedures may be user-supplied; others will be part of the
- library.
-
- For a programmer, this means that you're not watching what is happening;
- instead, you are requested by the toolkit to perform actions whenever
- necessary.
- So, you're not watching for 'raise window / close window / 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 I<callbacks>, and some of them you write yourself.
-
- =head1 First Requirements
-
- B<Perl> programs that use Tk need to include C<use Tk>.
- A program should also use C<use strict> and the B<-w> switch to ensure
- the program is working without common errors.
-
- Any Perl/Tk application starts by creating the Tk B<MainWindow>. You
- then create items inside the B<MainWindow>, and/or create new windows
- called B<Toplevel>s that also contain child items, before starting the
- B<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 I<geometry manager> like B<pack>; more information on
- this later. B<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 B<Label>
- and a B<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 C<-option> =E<gt> C<value> pairs.
-
- =head1 Widget creation
-
- Tk windows and widgets are hierarchical, S<i.e. one> window includes
- one or more other windows. You create the first Tk window using
- C<MainWindow-E<gt>new>. This returns a window handle, assigned to
- C<$mw> in the example above. Keep track of the main handle, commonly
- called a I<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 C<Label> method called from
- C<$mw> creates a B<Label> widget inside the B<MainWindow>. In the
- constructor call, you can specify various options; you can later add
- or change options for any widget using the B<configure> method, which
- takes the same parameters as the constructor. The one exception to
- the hierarchical structure is the B<Toplevel> constructor, which
- creates a new outermost window.
-
- After you create any widget (other than the B<MainWindow> or
- B<Toplevel>s, you must render it by calling B<pack>. (This is not
- entirely true; more later). If you do not need to refer to the widget
- after construction and packing, call B<pack> off the constructor
- results, as shown for the B<Label> and B<utton> in the example above. Note
- that the result of the compound call is the result of B<pack>, which
- is a valid Tk handle.
-
- Windows and widgets are deleted by calling B<destroy> on them;
- this will delete and un-draw the widget and all its children.
-
- =head1 Standard Tk widgets
-
- Here is an itemize of the standard Tk widget set.
-
- =over 4
-
- =item Button
-
- =item Canvas
-
- =item Checkbutton
-
- =item Entry
-
- =item Frame
-
- =item Label
-
- =item Labelframe
-
- =item Listbox
-
- =item Menu
-
- =item Menubutton
-
- =item Message
-
- =item Panedwindow
-
- =item Radiobutton
-
- =item Scale
-
- =item Scrollbar
-
- =item Spinbox
-
- =item Text
-
- =item Toplevel
-
- =back
-
- Perl/Tk provides an equal number of new widgets, above and beyond this
- core set.
-
- =over 4
-
- =item Adjuster
-
- =item Ballon
-
- =item BrowseEntry
-
- =item ColorEditor
-
- =item DirTree
-
- =item ErrorDialog
-
- =item FileSelect
-
- =item HList
-
- =item LabEntry
-
- =item LabFrame
-
- =item NoteBook
-
- =item Optionmenu
-
- =item Pane
-
- =item ProgressBar
-
- =item ROText
-
- =item Table
-
- =item TextUndo
-
- =item Tiler
-
- =item TList
-
- =item Tree
-
- =back
-
- =head1 Variables and callback routines
-
- Most graphical interfaces are used to set up a set of values and conditions,
- and then perform the appropriate action. The Tk toolkit is different
- 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 subroutine uses the method B<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";
- }
-
- =head1 The packer - grouping with Frame widgets
-
- In the examples above, you must have noticed the B<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
- I<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 B<-side =E<gt> 'right'>.
-
- Additional packing parameters specify the behavior of the widget when
- there is some space left in the B<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 B<-fill =E<gt> 'x'>, B<y>,
- or B<both>; for widgets that should grow, specify B<-expand =E<gt> 1>.
- These parameters are not shown in the example below; see the B<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;
-
- =head1 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 B<Toplevel> widget.
- Each window is independent; destroying a B<Toplevel> window does not
- affect the others as long as they are not a child of the closed
- B<Toplevel>. However, exiting the B<MainWindow> will destroy all remaining
- B<Toplevel> widgets 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');
- }
-
- =head1 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 B<listbox> with a scroll bar. Moving
- the scrollbar moves the B<listbox>. Scanning a B<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;
-
- =head1 Canvases and tags
-
- One of the most powerful widgets in Tk is the B<Canvas> window.
- In a <Canvas> window, you can draw simple graphics and include
- other widgets. 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 I<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 I<bind> actions to tags. For a properly designed (often
- structured) B<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";
- }
-