Cover | Table of Contents | Colophon
menu_item. Subroutine
XTextExtents determines several metrics about
menu_item, such as its overall width in pixels in
the current font and its pixel height, computed by summing the
maximum ascent and descent (the number of pixels above and below the
baseline, respectively). After accounting for the number of menu
items, border widths, and including some slop for good luck, we
arrive at the dimensions of the menu window itself, and its relative
(x, y) position in the MainWindow.
XTextExtents( font_info, menu_item, strlen( menu_item ),
&direction, &ascent, &descent, &overall );
menu_width = overall.width + 4;
menu_pane_height = overall.ascent + overall.descent + 4;
menu_height = menu_pane_height * menu_pane_count;
x = window_width - menu_width - ( 2 * menu_border_width );
y = 0;
XCreateSimpleWindow draws the menu with the proper
border and background colors, although nothing appears on the display
because the window hasn't yet been mapped.
theMenu = XCreateSimpleWindow( theDisplay, theWindow,
x, y, menu_width, menu_height,
menu_border_width, theBorderPixel,
theBackgroundPixel );% perl -v
This is perl, v5.6.0 built for i686-linux Copyright 1987-2000, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5.0 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using `man perl' or `perldoc perl'. If you have access to the Internet, point your browser at http://www.perl.com/, the Perl Home Page.
% perl -e "use Tk"
Can't locate Tk.pm in @INC (@INC contains: C:\PERL\lib\site ...
#!/usr/bin/perl
use Tk;
my $mw = MainWindow->new;
$mw->title("Hello World");
$mw->Button(-text => "Done", -command => sub { exit })->pack;
MainLoop;
my $mw = MainWindow->new;
title
method. If we hadn't used this method, the
text across the top of the window would be the same as the name of
the file containing the code, excluding any extension. For instance,
if the code were stored in a file named
hello_world, the string
"Hello_world" would appear across the title bar of the
application (Tk automatically capitalizes the first character for
you). Using the title method is not required, but
it makes the application look more polished.
Button.)
$bttn = $parent->Button(-text => "my text",
-command => sub { exit },
-width => 10,
-height => 10);
=> to make nice columns, depending on how much
time you have to press the spacebar.) Some people like to start the
option/value pairs on the next line and put the ending
); on its own separate line, after the last
option/value pair, which retains the comma for formatting ease:
$bttn = $parent->Button(
-text => "Exit",
-command => sub { exit },
-width => 10,
-height => 10,
);
warn statements throughout the code to
track progress and display intermediate results. We suggest using
warn rather than print for
three reasons: it adds the newline to the message automatically; the
output includes the line number of the warn
statement; and the output goes to STDERR, which is not normally
buffered, thus the output appears immediately. Furthermore, you type
fewer characters.
% hello_world
C:\>perl hello_world
\n on the end of the string to be printed, you
won't see the information actually printed until you quit the
program. You may have to unbuffer a file handle by setting the
special Perl variable $|. If you use
warn rather than print, these
drawbacks are eliminated.
O command and enable it by
setting the variable tkRunning:
[bug@Pandy atk]$ perl -de 0 Default die handler restored. Loading DB routines from perl5db.pl version 1.07 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(-e:1): 0 DB<1> O tkRunning tkRunning = '1' DB<2> use Tk DB<3> $mw = MainWindow->new DB<4> $b = $mw->Button(-text => 'Beep', -command => sub{$mw->bell}) DB<5> $b->pack DB<6> x $b 0 Tk::Button=HASH(0x82ed434) '_TkValue_' => '.button' DB<7> q
pack, place,
grid, and form.
$widget1->pack(); $widget2->place(); $widget3->grid(); $widget4->form( );
pack, it is difficult to have widgets stacked both
horizontally and vertically without grouping them in some fashion. We
use a Frame widget or another window (a Toplevel widget) to group
widgets inside a window.
MainWindow. The MainWindow is a special
form of a Toplevel widget. For more detailed information on how to
create/configure Frame and Toplevel widgets, see Chapter 11.
place, differences between the
geometry managers make it difficult (not entirely impossible, but
definitely not recommended) to use more than one geometry manager
within the same area. In
$mw, we can display many types of widgets, but if
we start using pack, we should continue to use
pack on all the widgets contained directly in
$mw. Don't switch to grid
in the middle, because the two geometry managers will get into a
race
condition: one will
create its layout, which affects the geometry calculations of the
other, which affects the layout of the first, causing it to recompute
its geometries, ad infinitum. However, let's assume our
MainWindow contains a Frame, which in turn contains other widgets. We
could use pack to pack the Frame inside the
MainWindow and then we could use grid to manage
the widgets inside the Frame. See Figure 2-1.
pack geometry
manager, our windows are similar to the wooden puzzle, because
widgets cannot overlap or cover each other, partially or completely
(see Figure 2-2). If a Button is packed in a
certain space on the window, the next Button (or any widget) will
have to fit around the already packed Button. Luckily, our windows
will be dealing only with rectangular shapes instead of funny-shaped
puzzle pieces.
pack with no
arguments and skip the rest of this chapter. Here it is again:
$widget->pack( );
pack method that
will change the way the widgets and the window look. As with anything
in Perl/Tk, the arguments are arranged in key/value pairs. So the
more sophisticated usage would be:
$widget->pack( [ option => value, ... ] );
pack options. We haven't covered all the
widgets used in this example, but hang in there; it's pretty
simple.
#!/usr/bin/perl -w
use Tk;
my $mw = MainWindow->new;
$mw->title("Bad Window");
$mw->Label(-text => "This is an example of a window that looks bad\nwhen you don't
send any options to pack")->pack;
$mw->Checkbutton(-text => "I like it!")->pack;
$mw->Checkbutton(-text => "I hate it!")->pack;
$mw->Checkbutton(-text => "I don't care")->pack;
$mw->Button(-text => "Exit",
-command => sub { exit })->pack;
MainLoop;grid geometry manager divides the window into a
grid composed of columns and rows starting at (0, 0) in the
upper-left corner. Figure 2-29 shows a sample grid.
grid divides the
screen into columns and rows. It looks a lot like a spreadsheet,
doesn't it? Each widget is assigned a grid cell using the
options available to grid.
grid method takes a list of widgets instead of
operating on only one widget at a time. Here is the generic usage:
$widget1->grid( [ $widget2, ... , ] [ option => value, ... ] );
$widget1->grid($widget2, $widget3);
grid call to display all three widgets. You can
also invoke grid on each widget independently,
just as you can pack. Each call to
grid will create another row in the window. So in
our example, $widget1,
$widget2, and$widget3 will be placed in the first
row. Another call to grid creates a second row.
This is what happens when you do not specify any additional options
to the grid call.
Tk::grid($widget1, $widget2, $widget3);
Tk::grid is the same as calling
a subroutine directly, whereas the method call searches the
widget's class hierarchy for a subroutine
grid. It's certainly possible that
$widget1 has its own special
grid method, which we would rudely bypass. Is this
a likely possibility? No. Just be aware when you make a procedural
versus a method call.
-rowplace geometry manager is different than
grid or pack. Rather than
referencing against a cell location or a window's side, most of
the time you'll be using a relative form of x and y
coordinates. You can also use place to overlap
portions of widgets, which isn't allowed in either
grid or pack.
place is similar to calling the other
geometry managers:
$widget->place( [ option => value, . . . ] );
place affect
how the widgets are put on the screen.
place:-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | '
nw
' | 'center'
-bordermode => '
inside
' | 'outside' | 'ignore'
-height =>
amount
-in => $window
$window instead of in the parent
that created it. Any relative coordinates or sizes will still refer
to the parent.
-relheight =>
ratio
form. Recently added into the
Perl/Tk distribution, form is a very different
geometry manager than those we've seen so far. To try and
compare it with what we already know, it behaves like a combination
of pack and place. Using
form, it is legal to overlap widgets (as you would
with place), but you can also display the widgets
relative to each other and stretch them out to fill the entire
available area and resize with the window (as you would with
pack). The combination of all these abilities
results in a powerful geometry manager.
form, each
edge of a widget can be attached to something: the container's
grid, another widget, or nothing at all. You can also use springs to
push your widgets around in the window based on the strength (or
weight) of the spring. As with the other geometry managers, you can
add padding to your widget.
form. The following
sections show you how to use these options to the best effect.
-bottom =>
attachment
-bottomspring =>
weight
-fill => 'x' | 'y' | 'both' | 'none'
pack
is
good for general purpose use and will be your choice about 95% of the
time.
grid
is
perfect for those situations in which you would like to create a
columnar layout similar to a spreadsheet. Options allow you to change
the sizes of rows and/or columns easily.
place
is
most useful when you want your widget to stay in a position or size
that is relative to the widget that created it. When used correctly,
it can be very powerful.
form is powerful, but difficult to get used to;
not for the faint of heart. Check future releases of the Tk module
for updates to this geometry manager.
-width of a Button without using
place. Always keep in mind the context in which
the option is used. Sometimes the functional difference is very
subtle.
perl myTkApp.pl -font "Times 12"
-font command-line option doesn't require
any changes to your Perl script. The -font option
works because of the way Tk::CmdLine works, described in Chapter 16. Note that you specify the
-font option after the name of the program to run.
As long as you haven't explicitly specified the font for any
widgets in your application, all widgets will use the new font.
*text*font=Courier
16 in the option database.
fontFamilies method
that displays various font specifications. So, before we get into the
details of creating a font definition, let's look at a program
that lets us play around with the fonts on our system. This program
is useful no matter what operating system you're on.
use Tk;
use Tk::BrowseEntry;
use strict;
my $mw = MainWindow->new(-title => 'Font Viewer');
my $f = $mw->Frame->pack(-side => 'top');
my $family = 'Courier';
my $be = $f->BrowseEntry(-label => 'Family:', -variable => \$family,
-browsecmd => \&apply_font)->pack(-fill => 'x', -side => 'left');
$be->insert('end', sort $mw->fontFamilies);
my $size = 24;
my $bentry = $f->BrowseEntry(-label => 'Size:', -variable => \$size,
-browsecmd => \&apply_font)->pack(-side => 'left');
$bentry->insert('end', (3 .. 32));
my $weight = 'normal';
$f->Checkbutton(-onvalue => 'bold', -offvalue => 'normal',
-text => 'Weight', -variable => \$weight,
-command => \&apply_font)->pack(-side => 'left');
my $slant = 'roman';
$f->Checkbutton(-onvalue => 'italic', -offvalue => 'roman',
-text => 'Slant', -variable => \$slant,
-command => \&apply_font)->pack(-side => 'left');
my $underline = 0;
$f->Checkbutton(-text => 'Underline', -variable => \$underline,
-command => \&apply_font)->pack(-side => 'left');
my $overstrike = 0;
$f->Checkbutton(-text => 'Overstrike', -variable => \$overstrike,
-command => \&apply_font)->pack(-side => 'left');
my $stext = 'Sample Text';
my $sample = $mw->Entry(-textvariable => \$stext)->pack(-fill => 'x');
&apply_font;
MainLoop;
sub apply_font {
# Specify all options for font in an anonymous array
$sample->configure(-font =>
[-family => $family,
-size => $size,
-weight => $weight,
-slant => $slant,
-underline => $underline,
-overstrike => $overstrike]);
}font option using an anonymous array. From
this we know a font consists of the following things:
'Courier',
'Times', and so on.
'normal' means it is not shown bold, and
'bold' makes the font thicker.
'roman' is used, and slanted if
'italic' is used.
-underline is true, the text will be underlined.
If false, the text will not be underlined.
*-helvetica-bold-r-*-*-*-240-*-*-*-*-*-*
$code_font = $mw->fontCreate('code', -family => 'courier',
-size => 12);
$code_font or by the name,
'code':
$mw->Button(-text => "Show Code", -font => 'code'); $mw->Button(-text => "Show Code2", -font => $code_font);
$code_font = $mw->fontCreate(-family => 'courier',
-size => 12);
fontConfigure method, using the font name or
reference as the first argument:
$mw->fontConfigure($code_font, -family => 'Verdana');
-font option will
also accept an anonymous array containing the right parts, with or
without the identifiers:
-font => ['courier', '14', 'bold']
# The same thing, but more verbose:
-font => [-family => 'courier',
-size => '14',
-weight => 'bold']
show_font sub using tags.
use Tk;
require Tk::TList;
require Tk::ROText;
use strict;
my $mw = MainWindow->new(-title => "Fonts");
$mw->minsize(700,400);
my $tl = $mw->Scrolled("TList", -font => ['Arial', '12'], -command => \&show_font)->
pack(-fill => 'both', -expand => 1);
# using a tlist, we have to insert each item individually
foreach (sort $mw->fontFamilies)
{
$tl->insert('end', -itemtype => 'text', -text => $_);
}
MainLoop;
# called when user double clicks on a font name in the tlist.
sub show_font
{
my ($index) = @_;
my $name = $tl->entrycget($index, -text);
my $top = $mw->Toplevel(-title => $name);
my $text = $top->Scrolled("ROText", -wrap => 'none')
->pack(-expand => 1, -fill => 'both');
$text->tagConfigure('number', -font => ['courier', '12']);
# since we don't know what font they picked, we dynamically
# create a tag w/that font formatting
$text->tagConfigure('abc', -font => [$name, '18']);
$text->insert('end', "abcdefghijklmnopqrstuvwxyz\
nABCDEFGHIJKLMNOPQRSTUVWXYZ\n1234567890.;,;(*!?')\n\n", 'abc');
foreach (qw/12 18 24 36 48 60 72/)
{
$text->tagConfigure("$name$_", -font => [$name, $_]);
$text->insert('end', "$_ ", 'number');
$text->insert('end',
"The quick brown fox jumps over the lazy dog. 1234567890\n", "$name$_");
}
}
fontCreate,
you can use the following methods.
fontActual to query
the font:
$mw->fontCreate('bigfont', -family => 'Arial', -size => 48);
%big = $mw->fontActual('bigfont');
print %big;
# prints:
-size 48 -overstrike 0 -underline 0
-weight normal -slant roman -family Arial
$size = $mw->fontActual('bigfont', -size);
print $size;
#prints:
48
fontConfigure:
if ($mw->fontConfigure('bigfont', -size) < 24) {
$mw->fontConfigure('bigfont', -size => 48);
}
# same as $mw->fontActual('bigfont');
%bigfont = $mw->fontConfigure('bigfont');
fontDelete:
$mw->fontDelete('bigfont');
fontFamilies:
@families = $mw->fontFamilies;
fontNames:
@definedfonts = $mw->fontNames;
fontNames method returns a list of object
references to Font objects. The list will be empty if there
aren't any fonts defined on your system. Keep in mind this list
contains only those fonts defined using the
fontCreate method.
fontMeasure. The answer is
given in pixels. Don't count on this figure to be the exact
size; it's more of an estimate.
print $mw->fontMeasure('bigfont', "SHORT"), "\n";
225
print $mw->fontMeasure('bigfont', "MUCH LONGER"), "\n";
480$button = $mw->Button->pack; $rb = $mw->Radiobutton->pack; $cb = $mw->Checkbutton->pack;
# Create a Button widget
$mw->Button(-text => 'Go', -command => \&go_go_go)->pack;
# Create a Checkbutton
$cb = $mw->Checkbutton(-text => 'Red', -onvalue => 'Red',
-offvalue => '')->pack;
# Create three Radiobuttons in Frame widget $f1
# Link them using $favcolor
foreach (qw/red blue green/) {
$f1->Radiobutton(-text => $_, -variable => \$favcolor,
-value => $_)->pack(-anchor => 'w');
}
-command expects
a callback, which we'll mention briefly in Section 4.9, but we won't fully describe
until Chapter 15.
$mw->Button(-text => 'Quit', -command => sub { print 'Bye!'; exit; })->pack;
-text and -command options. The
-text option lets the user know what the Button is
for, and the -command option makes something
happen when the user clicks the Button.
$b = $mw->Button(-text => 'Exit', -command => sub { exit; } )->pack;
# Use the same sub for many Buttons
$b = $mw->Button(-text => 'Red', -command => [\&change_color, 'red'])->pack;
$b = $mw->Button(-text => 'Blue',
-command => [\&change_color, 'blue'])->pack;
$b = $mw->Button(-text => 'Green',
-command => [\&change_color, 'green'])->pack;
-variable in addition to -text.
Using -variable gives you an easy way to find out
whether the Checkbutton is checked. (You will rarely use
-command with a Checkbutton):
$mw->Checkbutton(-text => 'Print Header', -variable => \$print_header);
sub print_document {
if ($print_header) {
# Code to print header here...
}
}
$print_header is 1 or 0. A
simple test will tell you if the Checkbutton was checked.
-text, -variable, and
-value options:
$group1 = 100; # set default value
foreach (qw/1 10 100 10000 100000 1000000/) {
$mw->Radiobutton(-text => '$' . $_, -variable => \$group1,
-value => $_)->pack(-side => 'left');
}
print "User selected: $group1";
$group1 relates all of the
Radiobuttons, making it so the user can select only one at a time.
Each Radiobutton must be given a -value to store
in $group1 (there is no default).
|
Option
|
Button
|
Checkbutton
|
Radiobutton
|
|---|---|---|---|
|
|
-text or
-textvariable option. The descriptive text string
should be short and simple.
-text option is the more common way to assign
a text string:
-text => 'Submit'