=head1 NAME
perl/Tk - Writing Tk applications in perl5.
=head1 DESCRIPTION
This manual page is
for
beginners. It assumes you know some perl,
and have got perl+Tk running.
Please run the
'widget'
demo
before
reading this text; it will teach you
the various widget types supported by Tk.
=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 will watch all events of interest,
and activate 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.
=head1 First requirements
Any perl program that uses Tk needs 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 Tk application starts by creating the Tk main window. You then create
items inside the main window, or create new windows,
before
starting the
mainloop.
(You can also create more items and windows
while
you're running.)
The items will be shown on the display
after
you C<
pack
> them;
more info on this later.
Then you
do
a Tk mainloop; this will start the GUI and handle all events.
That's your application.
A trivial one-window example is show below:
my
$main
= new MainWindow;
$main
->Label(
-text
=>
'Hello, world!'
)->
pack
;
$main
->Button(
-text
=>
'Quit'
,
-command
=>
sub
{
exit
}
)->
pack
;
MainLoop;
Please run this example. It shows you two items types also shown in the
widget demo; it also shows you how items are created and packed.
Finally, note the typical Tk style using C<-option> =E<gt> C<value> pairs.
=head1 Item creation
Tk windows and widgets are hierarchical, S<i.e. one> includes one or more
others. You create the first Tk window using C<new MainWindow>.
This returns a window handle, assigned to C<
$main
> in the example above.
Keep track of the main handle.
You can
use
any Tk handle to create
sub
-items 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<
$main
> creates a
label widget inside the main window. In the constructor call, you can specify
various options; you can later add or change options
for
any widget
using the C<configure> method, which takes the same parameters as the
constructor.
The one exception to the hierarchical structure is the C<Toplevel> constructor,
which creates a new outermost window.
After you create any widget, you must render it by calling C<
pack
>. (This
is not entirely true; more info later). If you
do
not need to refer to
the widget
after
construction and packing, call C<
pack
> off the constructor
results, as shown
for
the label and button in the example above.
Note that the result of the compound call is the result of C<
pack
>,
which is not a valid Tk handle.
Windows and widgets are deleted by calling C<destroy> on them;
this will
delete
and un-draw the widget and all its children,
with
the exception of Toplevel windows.
=head1 Standard Tk types
=over 4
=item Button
=item Radiobutton
=item Checkbutton
=item Listbox
=item Scrollbar
=item Entry
=item Text
=item Canvas
=item Frame
=item Toplevel
=item Scale
=item Menu
=item Menubutton
=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:
(yes, this is obviously dumb code)
print
"Please type a font name\n"
;
my
$font
= <>;
chop
$font
;
print
"Please type a file name\n"
;
my
$filename
= <>;
chop
$filename
;
print
"Type <1> to fax, <2> to print\n"
;
my
$option
= <>;
chop
$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 is Tk.
Note the
use
of callbacks. Note, also, that Tk handles the
values
, and
the subroutine uses C<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.
my
$main
= new MainWindow;
$main
->Label(
-text
=>
'Print file'
)->
pack
;
my
$font
=
$main
->Entry(
-width
=> 10);
$font
->
pack
;
my
$filename
=
$main
->Entry(
-width
=> 10);
$filename
->
pack
;
$main
->Button(
-text
=>
'Fax'
,
-command
=>
sub
{do_fax(
$filename
,
$font
)}
)->
pack
;
$main
->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_val\n"
;
}
sub
do_print {
my
(
$file
,
$font
) =
@_
;
$file
= get
$file
;
$font
= get
$font
;
print
"Sending file $file to printer in $font\n"
;
}
=head1 The packer. Grouping and frames.
In the examples above, you must have noticed the C<
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 widget placement manager;
the I<packer> is the most useful placement manager.
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 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 space, specify B<-fill =E<gt>
'x'
>, B<y>, or B<both>;
for
widgets that should grow, specify B<-expand =E<gt>
'yes'
>. These
parameters are not shown in the example below; see the widget demo.
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 (and to play games
with
borders and colors).
The example below shows the
use
of
pack
and frames:
my
$main
= new MainWindow;
$main
->Label(
-text
=>
'At the top (default)'
)->
pack
;
$main
->Label(
-text
=>
'At the bottom'
)->
pack
(
-side
=>
'bottom'
);
$main
->Label(
-text
=>
'The middle remains'
)->
pack
;
my
$top1
=
$main
->Toplevel;
$top1
->Label(
-text
=>
'Left'
)->
pack
(
-side
=>
'left'
);
$top1
->Label(
-text
=>
'Right'
)->
pack
(
-side
=>
'right'
);
$top1
->Label(
-text
=>
'?Bottom?'
)->
pack
(
-side
=>
'bottom'
);
my
$top2
=
$main
->Toplevel;
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
read
before
,
you can create more outermost windows by using Toplevel. Each window
is independent; exiting a toplevel window does not affect the others.
Exiting the main window will end the application.
The example below shows a trivial three-window application:
my
$main
= new MainWindow;
fill_window(
$main
,
'Main'
);
my
$top1
=
$main
->Toplevel;
fill_window(
$top1
,
'First top-level'
);
my
$top2
=
$main
->Toplevel;
fill_window(
$top2
,
'Second top-level'
);
MainLoop;
sub
fill_window {
my
(
$window
,
$header
) =
@_
;
$window
->Label(
-text
=>
$header
)->
pack
;
$window
->Button(
-text
=>
'close'
,
-command
=>
sub
{destroy
$window
}
)->
pack
(
-side
=>
'left'
);
$window
->Button(
-text
=>
'exit'
,
-command
=>
sub
{
exit
}
)->
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 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.
my
$main
= new MainWindow;
my
$box
=
$main
->Listbox(
-relief
=>
'sunken'
,
-width
=> -1,
-height
=> 5,
-setgrid
=>
'yes'
);
my
@items
=
qw(One Two Three Four Five Six Seven
Eight Nine Ten Eleven Twelve)
;
foreach
(
@items
) {
$box
->insert(
'end'
,
$_
);
}
my
$scroll
=
$main
->Scrollbar(
-command
=> [
'yview'
,
$box
]);
$box
->configure(
-yscrollcommand
=> [
'set'
,
$scroll
]);
$box
->
pack
(
-side
=>
'left'
,
-fill
=>
'both'
,
-expand
=>
'yes'
);
$scroll
->
pack
(
-side
=>
'right'
,
-fill
=>
'y'
);
MainLoop;
=head1 Canvases and tags
One of the most powerful window types in Tk is the 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) 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.
my
$main
= new MainWindow;
my
$canvas
=
$main
->Canvas;
$canvas
->
pack
(
-expand
=>
'yes'
,
-fill
=>
'both'
);
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'
);
$canvas
->
bind
(
'circle'
,
'<1>'
=>
sub
{handle_circle(
$canvas
)});
$canvas
->
bind
(
'blue'
,
'<Double-1>'
=>
sub
{handle_blue(
$canvas
)});
MainLoop;
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
);
}
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"
;
}