![]() |
|
![]() |
Learning Perl/Tk: Graphical User Interfaces with PerlBy Nancy Walsh1st Edition January 1999 1-56592-314-6, Order Number: 3146 376 pages, $32.95 |
Sample Chapter 2:
Geometry Management
To display widgets on the screen, they must be passed to a geometry manager. The geometry manager controls the position and size of the widgets in the display window. There are several geometry managers available with Perl/Tk:
pack
,place
, andgrid
.All three geometry managers are invoked as methods on the widget, but they all have their own methodologies and arguments to change where and how the widgets are put on the screen:
$widget1->pack(); $widget2->place(); $widget3->grid();
When you organize the widgets in your window, it is often necessary to separate groups of widgets to get a certain look and feel. For instance, when you use
pack()
, it is difficult to have widgets stacked both horizontally and vertically without grouping them in some fashion. We group widgets by using a frame widget inside a window or by using another window (a toplevel widget).We create our first window by calling
MainWindow
. TheMainWindow
is a special form of a toplevel widget. For more detailed information on how to create/configure frames and toplevel widgets, see Chapter 12, Frames, and Chapter 13, Toplevel Widgets.Because of the differences between the three geometry managers, it is difficult (not entirely impossible, but definitely not recommended) to use more than one geometry manager within the same area. In our
$mw
, I can display many types of widgets, but if I start usingpack()
, I should continue to usepack()
on all of the widgets contained directly in$mw
. I wouldn't want to switch in the middle to usinggrid()
. Because a window can contain a frame, which in turn contains other widgets, we usepack()
to pack the frame inside the main window and then we could usegrid()
to manage the widgets inside the frame. See Figure 2-1.
Figure 2-1. Frame within a window that uses a different geometry manager
![]()
Although the different geometry managers have their own strengths and weaknesses, the most commonly used is
pack()
, so I'll discuss it first and in the most detail. Thegrid()
geometry manager was under development as I was writing this book.grid
has been improved greatly with the release of Tk 8.0 and subsequent porting to Perl. Theplace()
geometry manager is the most tedious to use because you have to determine exact coordinates for every single widget.Pack
Remember when you were a small child and you had those wooden puzzles to put together? They often had cute little pictures of animals on them. Each piece in the puzzle had exactly one place where it could go, and there weren't any overlaps allowed between pieces.
With the
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 move around the already packed button. Luckily, our windows will only be dealing with rectangular shapes instead of funny-shaped puzzle pieces.
Figure 2-2. Overlap error
![]()
The order in which you pack your widgets is very important because it directly affects what you see on the screen. Each frame or toplevel maintains a list of items that are displayed within it. This list has an order to it; if widget A is packed before widget B, then widget A will get preference. This will become clear as we go through some examples. You will often get a very different look to your window just by packing the widgets in a different order.
If you don't care what the window looks like and how the widgets are put in it, you can use
pack()
with no arguments and skip the rest of this chapter. Here it is again:$widget->pack();
To make your window look nicer and more manageable (and user friendly), there are arguments that can be sent to the
pack
method that will change the way the widgets and the window looks. As with anything in Perl/Tk, the arguments are arranged in pairs. So the more sophisticated usage would be:$widget->pack( [ option => value, ... ] );
Here is the code to create a window that doesn't use any options to
pack()
. Figure 2-3 shows the resulting window (I know 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;
Figure 2-3. Window with widgets managed by pack
![]()
We can alter the preceding code and add some options to the
pack()
calls that will make our window look much nicer:#!/usr/bin/perl -w
use Tk;
my $mw = MainWindow->new;
$mw->title("Good Window");
$mw->Label(-text => "This window looks much more organized, and less haphazard\nbecause we used some options to make it look nice")->pack;
$mw->Button(-text => "Exit",
-command => sub { exit })->pack(-side => 'bottom',
-expand => 1,
-fill => 'x');
$mw->Checkbutton(-text => "I like it!")->pack(-side => 'left',
-expand => 1);
$mw->Checkbutton(-text => "I hate it!")->pack(-side => 'left',
-expand => 1);
$mw->Checkbutton(-text => "I don't care")->pack(-side => 'left',
-expand => 1);
MainLoop;
Figure 2-4 shows the much more organized window.
Figure 2-4. Window with widgets managed by pack using some options
![]()
Using
pack()
allows you to control:
- Position in the window relative to the window or frame edges
- Size of widgets, relative to other widgets or absolute
- Spacing between widgets
- Position in the window's or frame's widget list
The options, values, and defaults are listed and discussed in the following section.
Pack Options
This list shows all the options available when you call
pack()
. The default values are shown in bold (which indicates if you don't use that option, you'll get the effects of that value for that option).
-side => 'left' | 'right' | '
top' | 'bottom'
- Puts the widget against the specified side of the window or frame
-fill => '
none' | 'x' | 'y'| 'both'
- Causes the widget to fill the allocation rectangle in the specified direction
-expand => 1 |
0
- Causes the allocation rectangle to fill the remaining space available in the window or frame
-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | '
center'
- Anchors the widget inside the allocation rectangle
-after => $otherwidget
- Puts
$widget
after$otherwidget
in packing order
-before => $otherwidget
- Puts
$widget
before$otherwidget
in packing order
-in => $otherwindow
- Packs
$widget
inside of$otherwindow
rather than the parent of$widget
, which is the default
-ipadx =>
amount
- Increases the size of the widget horizontally by amount ✕ 2
-ipady =>
amount
- Increases the size of the widget vertically by amount ✕ 2
-padx =>
amount
- Places padding on the left and right of the widget
-pady =>
amount
- Places padding on the top and bottom of the widget
Positioning Widgets
Each window (or frame) has four sides to it: top, bottom, left, and right. The packer uses these sides as points of reference for widgets. By default,
pack()
places the widgets against the top of the toplevel or frame.You can control what side a widget is placed against by using the
-side
option:-side => 'left' | 'right' | 'top' | 'bottom'
For example, if we would like our button against the left edge of the window, we can specify
-side =>
'left
'.Using our Hello World example as a base, let's look at what happens when we pack our button against the different sides. The only line we will change is the
->pack
part of the Button creation line. We'll also change the "Hello World" string in the$mw->title
command to easily show the new options to pack.
$mw->Button(-text => 'Done', -command => sub { exit }) ->pack(-side => 'top'); ->pack;OR
$mw->Button(-text => 'Done', -command => sub { exit }) ->pack;
$mw->Button(-text => 'Done', -command => sub { exit }) ->pack(-side => 'bottom');
$mw->Button(-text => 'Done', -command => sub { exit }) ->pack(-side => 'left');
$mw->Button(-text => 'Done', -command => sub { exit }) ->pack(-side => 'right');
![]()
The windows shown here have been made a bit larger to emphasize the difference that using alternative values for
-side
makes. Normally, the window will be only as large as required to show the button. When you are deciding which way to place widgets in a window, it is always a good idea to see what happens when you make the window both larger and smaller. Make sure the behavior you get is what you want.So far,
pack()
seems pretty simple, but what if you want to put more than one button in your application? What happens when we simply add more buttons?$mw->Button(-text => 'Done1', -command => sub { exit })->pack;
$mw->Button(-text => 'Done2', -command => sub { exit })->pack;
$mw->Button(-text => 'Done3', -command => sub { exit })->pack;
$mw->Button(-text => 'Done4', -command => sub { exit })->pack;
Since the default
-side
istop
, we would expect them to all be mushed up against the top of the window, right? Sort of. The packer allocates space for each widget and then manipulates the widget inside that space and the space inside the window.Figure 2-5 shows what the window with the four Done buttons looks like; the next section explains why.
Figure 2-5. Four buttons packed with default settings
![]()
Allocation Rectangles
When given an item to pack, the packer first looks to see which side (top, bottom, right, or left) to use. It then sets aside an invisible rectangular area across the length of that side for use only by that widget.
In Figure 2-6, the solid-line rectangle represents our empty window (or frame), and the dotted-line rectangle is the rectangular area that the packer sets aside for the first button. It actually does go all the way across the width or height of the window, but to make it easier to see, it's shown a little indented.
Figure 2-6. Rectangular areas set aside by packer when using -side => `top' and -side => `left'
![]()
The dimensions for the dotted-line box, which we'll call the allocation rectangle, are calculated based on the size of the requesting widget. For both the top and bottom sides, the allocation rectangle is as wide as the window and only as tall as the widget to be placed in it. For the right and left sides, the allocation rectangle is as tall as the window but only as wide as required to fit the widget.
Our examples so far have used buttons in which the text of the button determines the width of the button. If we create a button with the text "Done" on it and one with the text "Done, Finished, That's it," the second button is going to be much wider than the first. When these two buttons are placed up against either the right or left side of the window, the second button would have a wider allocation rectangle than the first. If we placed those same two buttons against the top and the bottom, the allocation rectangles would be the same height and width because the width is determined by the window, not the widget.
After the size of the allocation rectangle is determined, the widget is placed within the allocation rectangle according to other options passed and/or the default values of those options. I will go over those options and how they can affect the allocation rectangle later.
Once the first widget has been placed in the window, the amount of area available for subsequent allocation rectangles is smaller because the first allocation rectangle has used some of the space (see Figure 2-7).
Figure 2-7. Second allocation rectangle when default side `top' is used
![]()
When more than one button is placed against different sides in the same window, the results will vary depending on the order used.
We'll start by placing one button along the top, one along the bottom, and then buttons right and left:
$mw->Button(-text => "TOP", -command => sub { exit })
->pack(-side => 'top');
$mw->Button(-text => "BOTTOM", -command => sub { exit })
->pack(-side => 'bottom');
$mw->Button(-text => "RIGHT", -command => sub { exit })
->pack(-side => 'right');
$mw->Button(-text => "LEFT", -command => sub { exit })
->pack(-side => 'left');
The allocation rectangles for this window would look like the diagram in Figure 2-8.
Figure 2-8. Allocation rectangles for four buttons
![]()
Figure 2-9 shows what the actual window looks like, both normal size and resized so it's a bit larger.
Figure 2-9. Four buttons placed around the sides of the window
![]()
Filling the Allocation Rectangle
Normally, the widget is left at the default size, which is usually smaller than the allocation rectangle created for it. If the
-fill
option is used, the widget will resize itself to fill the allocation rectangle according to the value given. The possible values are:-fill => 'none' | 'x' | 'y' | 'both'
Using the value
'x'
will resize the widget in the x direction. Likewise,'y'
will cause the widget to resize in the y direction. Using-fill => 'both'
is a good way to see exactly what size and placement was given to the allocation rectangle because'both'
resizes the widget in both x and y directions. Using our four-button example again, we'll specify-fill => 'both'
.$mw->Button(-text => "TOP", -command => sub { exit })
->pack(-side => 'top', -fill => 'both');
$mw->Button(-text => "BOTTOM", -command => sub { exit })
->pack(-side => 'bottom', -fill => 'both');
$mw->Button(-text => "RIGHT", -command => sub { exit })
->pack(-side => 'right', -fill => 'both');
$mw->Button(-text => "LEFT", -command => sub { exit })
->pack(-side => 'left', -fill => 'both');
Figure 2-10 shows the resulting window.
Figure 2-10. Four buttons packed to each side using -fill => `both'
![]()
If we switch the button we create first, we get a different result. The window in Figure 2-11 was created by packing the widgets in this order: left, right, top, bottom.
Figure 2-11. Four buttons packed to each side in a different order using -fill => `both'
![]()
Figure 2-12 demonstrates yet another order, which really shows that the allocation rectangles change size depending on what gets packed first.
Figure 2-12. Four buttons packed in order of top, right, bottom, and left
![]()
A common use of
-fill
is on widgets with scrollbars: listbox, canvas, and text. Usually, the scrollbars are along the edge of the window, and you want the listbox to fill the remaining area. See Chapter 6, Scrollbars, and Chapter 7, The Listbox Widget, for more information.Expanding the Allocation Rectangle
The
-expand
option manipulates the allocation rectangle and not the widget inside it. The value associated with-expand
is a boolean value.-expand => 1 | 0
Given a true value, the allocation rectangle will expand into any available space left over in the window depending on which side the widget was packed.
Widgets packed with side right or left will expand in the horizontal direction. Widgets packed with side top or bottom will expand in the vertical direction. If more than one widget is packed with
-expand
turned on, the extra space in the window is divided evenly among all the allocation rectangles that want it.In Figure 2-9 or See , you saw that there was some space left in the center of the window that wasn't occupied by any widget. If we change the code and add
-expand => 1
to the list of pack options for each button, the result is the window in Figure 2-13.
Figure 2-13. Four buttons using the -expand => 1 and -fill => `both' options
![]()
Note that Figure 2-13 left the
-fill => 'both'
option in the code. If we omit the-fill
option, the buttons stay their original size, but the allocation rectangles (which are invisible) take over the extra space in the window (see Figure 2-14).
Figure 2-14. Four buttons using -expand => 1 and -fill => `none'
![]()
In Figure 2-14, the buttons are centered in their allocation rectangles because of the default value of the
-anchor
option, which is'center'
.Anchoring a Widget in Its Allocation Rectangle
The anchor option manipulates the widget inside the allocation rectangle by anchoring it to the place indicated by the value passed in. It uses the points of a compass as a reference.
-anchor => 'e' | 'w' | 'n' | 's' | 'ne' | 'nw' | 'se' | 'sw' | 'center'
Figure 2-15 shows those locations in an example allocation rectangle.
Figure 2-15. Allocation rectangle with -anchor points labeled
![]()
The default for
-anchor
is'center'
, which keeps the widget in the center of its allocation rectangle. Unless the-expand
option is set to a true value, this won't seem to change much of anything in the window. As seen in Figure 2-16, which shows the result of using the-expand => 1
option, it is obvious that the widget sticks to that center position when the window is resized.
Figure 2-16. Default behavior of -anchor with -expand set to 1
![]()
If all other defaults are used to pack the widget, Figure 2-17 shows what
-anchor => 'e' and -anchor => 'w'
does.
Figure 2-17. Examples of -anchor => `e' and -anchor => `w'
![]()
Remember that the allocation rectangle is created based on which side the widget is packed against, so certain combinations will appear to have not had any effect. For example:
$mw->Button(-text => "Done", -command => sub { exit })
->pack(-side => 'top', -anchor -> 'n');
This code fragment will leave the widget exactly where it was if the
-anchor
option had not been specified because the allocation rectangle does not change size at all. If the-expand
option is also specified, then when the window is resized, the widget would stick to the north side of the window. If-anchor => 's'
had been specified, when the window is resized, the widget would stick to the south side of the window.The
-anchor
option is more often used to line up several widgets in a row. Figure 2-18 and Figure 2-19 show two common examples.
Figure 2-18. Window with three buttons all packed with -side => `top', -anchor => `w'
![]()
Figure 2-19. Windows with three buttons all packed with -side => `left', -anchor => `n'
![]()
Sometimes, when
-side
and-anchor
are used together, the results don't seem to be what you would expect at first glance. Always keep in mind that invisible allocation rectangle and how it affects what you see on the screen.Widget Order in the Window
Each window that has widgets packed into it keeps track of those widgets in an ordered list. The order of this list is normally determined by the order in which the widgets were packed. The last item packed is the last item in the list. Using the
-after
option, you can change the default order by specifying which widget should be placed after your new widget. On the opposite end, if you use the-before
option, you can put the new widget before a previously packed widget:-after => $otherwidget
-before => $otherwidget
As an example, let's create four buttons (
$widget1
,$widget2
,$widget3
,$widget4
) and only pack three to begin with. Thepack
command for$widget4
might then be:$widget4->pack(-after => $widget1);
Figure 2-20 shows two windows: one before
$widget4
is packed and one after$widget4
is packed.
Figure 2-20. On left: the window with three buttons packed in order. On right: the button with Done4 label was packed using -after => $widget1
![]()
If we want to put
$widget4
in front of$widget1
, we use this command, and see the results in Figure 2-21.$widget4->pack(-before => $widget1);
Figure 2-21. Button with Done4 label was packed using -before => $done1
![]()
Padding the Size of the Widget
The final way to force pack to size the widget is to use the padding options. The first set of padding options affects the widget itself by adding to its default size. Different amounts can be added in the x and y direction, or they can be the same. To specify how much padding should occur in the x direction, use the
-ipadx
option:-ipadx => amount
Specify padding for the y direction like this:
-ipady => amount
The amount is a number that is a valid screen distance. I'll discuss the definition of a valid screen distance in the next section.
Both the
-ipadx
and-ipady
options change the size of the widget before the allocation rectangle is calculated.-ipadx
adds the amount specified to both the right and left sides of the widget. The overall width of the widget would increase by (2 x amount).-ipady
adds to the top and bottom of the widget, causing the overall height of the widget to increase by (2 x amount). Figure 2-22 shows how the-ipadx
and-ipady
options affect a button.
Figure 2-22. The Done1 button was created with options: -ipadx => 10, -ipady => 10
![]()
The other kind of padding is inserted between the edge of the widget and the edge of the allocation rectangle and is done with the
-padx
and-pady
options:-padx => amount
-pady => amount
Using
-padx
and-pady
does not affect the size of the widget, but it does affect the size of the allocation rectangle. It acts as a buffer around the widget, protecting it from having to touch other widgets. Figure 2-23 shows the effects of using-padx
and-pady
.
Figure 2-23. The Done1 button was created with options -padx => 10, -pady => 10
![]()
A good way to remember the difference between
-ipadx/y
and-padx/y
is that the "i" stands for "inside the widget" or "internal padding."Valid screen distances
Many times you'll see options that require values specified in screen units (or what is called a valid screen distance). The options
-ipadx
and-ipady
are examples of this type of option. Always check to see what value the option actually requires.A screen unit is a number followed by a designation for the unit to use. If there is no designation, the units are in pixels. Table 2-1 shows all the possibilities.
Table 2-1: Valid screen units Designator
Meaning
Examples
(none)
Pixels (default)
20
,30
,"20"
,"40"
c
Centimeters
'3c'
,'4c'
,"3c"
i
Inches
'2i'
,"3i"
m
Millimeters
'4m'
,"4m"
p
Printer points (1/72 inch)
"72p"
,'40p'
To use these designators, it is necessary to use quotes (either single or double) around the value. Here are some examples:
$button->pack(-ipadx => 20); # 20 pixels
$button->pack(-ipadx => '20'); # Also 20 pixels
$button->pack(-ipadx => "1i"); # 1 inch
$button->pack(-ipadx => '1m'); # 1 millimeter
$button->pack(-ipadx => 1); # 1 pixel
$button->pack(-ipadx => "20p"); # 20 printer points
Remember that a "p" designator does not stand for pixels, but printer points. I recommend always using pixels as your unit of measure. Different screens display different resolutions; one screen might display an actual inch and another might display something else.
Displaying in a Parent Other Than Your Own
By default, when a widget is packed, it is packed inside the region that created it. Sometimes it is necessary to display a widget inside a different region. Use the
-in
option to do so:-in => $otherwindow
It puts the new widget at the end of the packing order for the
$otherwindow
and displays it accordingly. All other options specified in thepack()
call still apply.Methods Associated with Pack
There are a few methods that are used in conjunction with the
pack
geometry manager. They allow the programmer to get information about either the widget that has been packed or the parent widget in which other widgets are packed.Unpacking a widget
To unpack a widget from a window or frame, use the
packForget
method:$widget->packForget();
packForget
makes it look like the widget disappears. The widget is not destroyed, but it is no longer managed by pack. The widget is removed from the packing order, so if it were repacked later, it would appear at the end of the packing order.Retrieving pack information
To return a list containing all the pack-configuration information about a widget, use
packInfo
:@list = $widget->packInfo();
The format of the list is in option/value pairs. The first pair in the list is
-in
and the current window that contains$widget
(usually also the parent). This is an example of the information returned frompackInfo
:-in MainWindow=HASH(0x818dcf4) -anchor n -expand 0 -fill none -ipadx 0 -ipady 0 -padx 10 -pady 10 -side left
From this, we can tell that we packed our
$widget
into the main window rather than a frame. Since the list has a "paired" quality to it, we could easily store the result frompackInfo
in a hash and reference the different option values by using a key to the hash:%packinfo = $widget->packInfo;
print "Side used: ", $packinfo{-side}, "\n";
Disabling and enabling automatic resizing
When you put a widget inside a window, the window (or frame) will resize itself to accommodate the widget. If you are dynamically placing widgets inside your window while the program is running, the window will seem to bounce from size to size. You can turn off this behavior by using
packPropagate
on the frame or toplevel widget:$widget->packPropagate(0);
If set to
0
or'off'
,packPropagate
changes the behavior of the widget so that it doesn't resize to accommodate items packed inside of it. When a false value is sent topackPropagate
before widgets are placed inside it, this automatic resizing doesn't happen, so you can't see any of the widgets placed inside the parent until it is manually resized. If you callpackPropogate
after the widgets have been placed inside it, the widget will ignore any size changes from its child widgets.Listing widgets
You can determine the widgets your frame or toplevel holds with the
packSlaves
method:@list = $parentwidget->packSlaves();
packSlaves
returns an ordered list of all the widgets that were packed into the$parentwidget
. An empty string (or empty list) is returned if no widgets were packed into$parentwidget
.The list returned from
packSlaves
looks like this:Tk::Button=HASH(0x81b2970) Tk::Button=HASH(0x8116ccc) Tk::Button=HASH(0x81bcdd4)
Each item is a reference to a packed widget and can be used to configure it. For example, you can increase the size of each widget by 20 in both the x and y directions by looping through it and "packing" it with new information. Using our good window example in Figure 2-4, we can add a button that will contain a subroutine that uses
packSlaves
:$mw->Button(-text => "Enlarge",
-command => \&repack_kids)->pack(-side =>
'
bottom'
,-anchor =>
'
center'
);sub repack_kids {
my @kids = $mw->packSlaves;
foreach (@kids) {
$_->pack(-ipadx => 20, -ipady => 20);
}
}
Figure 2-24 shows the resulting window.
Figure 2-24. Window before pressing Enlarge button
![]()
Let's look at what happens when we press the Enlarge button. As shown in Figure 2-25, all the widgets are now repacked with additional parameters of
-ipadx => 20, -ipady => 20
. These new options are in addition to any other parameters the widgets were packed with before. If an option is repeated, the last one specified overrides the previous ones.
Figure 2-25. Window after pressing Enlarge button
![]()
The window is suddenly huge! Subsequent presses of the Enlarge button will do nothing more to the window because all the widgets already have an
-ipadx
and-ipady
of 20. If we wanted to always add 20 to the values of-ipadx
and-ipady
, we would have to request the current values and add 20 to them. Here's the code for that:sub repack_kids {
my @kids = $mw->packSlaves;
foreach (@kids) {
%packinfo = $_->packInfo();
$_->pack(-ipadx => 20 + $packinfo{"-ipadx"},
-ipady => 20 + $packinfo{"-ipady"});
}
}
We use
packInfo
to get the current configuration and add 20 to that value.Grid
The
grid
geometry manager divides the window into a grid composed of columns and rows starting at 0,0 in the upper left-hand corner. Figure 2-26 shows a sample grid.
Figure 2-26. Diagram showing window divided into grid
![]()
Rather than using the sides of a window as reference points,
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 togrid()
.The
grid()
method takes a list of widgets instead of operating on only one widget at a time.[1] Here is the generic usage:$widget1->grid( [ $widget2, ... , ] [ option => value, ... ] );
A specific example:
$widget1->grid($widget2, $widget3);
Instead of using three separate calls, you can use one
grid()
call to display all three widgets. You can also invokegrid()
on each widget independently just as you canpack()
. Each call togrid()
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 would create a second row. This is what happens when you do not specify any additional options to thegrid()
call.For greater control, you can specify explicit
-row
and-column
options for each widget in the window. I will cover these options later.These assumptions are made when additional options are not specified:
- The first widget in the row (for example,
$widget1
in the preceding example) invokes thegrid()
command.- All remaining widgets for that row will be specified as arguments to the
grid()
command.- Each additional call to
grid()
will add another row to the display.- Special characters can be used to change the
-columnspan
and-rowspan
of the widget without using -columnspan or -rowspan explicitly.A few examples will help demonstrate. Each call to
grid()
will create another row, so we know we have two rows in the following example:# Create two rows, each with four widgets
$widget1->grid($widget2, $widget3, $widget4);
$widget5->grid($widget6, $widget7, $widget8);
In this example, we have created four rows and there is only one widget in each row:
# Create four rows, each with one widget
$widget1->grid();
$widget2->grid();
$widget3->grid();
$widget4->grid();
We can also create widgets as we go:
$mw->Button(-text => 'Button1', -command => \&call1)->grid(
$mw->Button(-text => 'Button2', -command => \&call2),
$mw->Button(-text => 'Button3', -command => \&call3),
$mw->Button(-text => 'Button4', -command => \&call4));
Pay careful attention because the second, third, and fourth calls to
Button
are inside the call togrid()
. All four of the buttons will be placed in the first row. If we executed the same exact command again, the new widgets would be placed in the next row.Special Characters
There are several special characters that can be used to alter the way the widgets are gridded in the window. Each special character serves as a type of placeholder that indicates what to do with that position in the grid:
"-"
(a minus sign)- Tells grid that the widget specified right before this one in the list should span this column as well. To span more than one column, place a
"-"
in each widget position to span. A"-"
may not follow a"^"
or an"x"
."x"
- Effectively leaves a blank space where a widget would otherwise be placed.
"^"
- A widget in row x will span row x and x + 1 when this character is placed in the
grid
command for row x + 1 in that row/column position. The number of"^"
characters must match the number of columns the widget spans in row x. Similar to"-"
, but goes down, not across.[2]The following sections include some examples that illustrate what the special characters do.
Spanning columns
The following bit of code creates three rows of buttons. The first two rows are normal, and in the third, the second button spans three columns. Each
"-"
character adds one to the number of columns the button uses, and the default is 1. So the original column and two hyphens ("-"
,"-"
) indicate that there are three columns to span. The-sticky
option is necessary for the widgets to stick to the sides of the cells it spans. If the-sticky
option had been left out, the button would be centered across the three cells it spans.$mw->Button(-text => "Button1", -command => sub { exit })->grid
($mw->Button(-text => "Button2", -command => sub { exit }),
$mw->Button(-text => "Button3", -command => sub { exit }),
$mw->Button(-text => "Button4", -command => sub { exit }));
$mw->Button(-text => "Button5", -command => sub { exit })->grid
($mw->Button(-text => "Button6", -command => sub { exit }),
$mw->Button(-text => "Button7", -command => sub { exit }),
$mw->Button(-text => "Button8", -command => sub { exit }));
$mw->Button(-text => "Button9", -command => sub { exit })->grid
($mw->Button(-text => "Button10", -command => sub { exit }),
"-", "-", -sticky => "nsew");
The resulting window is shown in Figure 2-27.
Figure 2-27. Example of column spanning using the "-" character
![]()
Empty cells
The
"x"
character translates to "skip this space" and leaves a hole in the grid. I removed the line that created Button6 and replaced it with an "x" in the following code. The cell for it is still there, it just doesn't contain a widget.$mw->Button(-text => "Button1", -command => sub { exit })->grid
($mw->Button(-text => "Button2", -command => sub { exit }),
$mw->Button(-text => "Button3", -command => sub { exit }),
$mw->Button(-text => "Button4", -command => sub { exit }));
$mw->Button(-text => "Button5", -command => sub { exit })->grid
("x",
$mw->Button(-text => "Button7", -command => sub { exit }),
$mw->Button(-text => "Button8", -command => sub { exit }));
The resulting window is shown in Figure 2-28.
Figure 2-28. Leaving an empty cell between widgets
![]()
Grid Options
The rest of the options are similar to those used with
pack()
:
"-"
- Special character used in
grid
widget list. Increasescolumnspan
of widget prior to it in widget list.
"x"
- Special character used in
grid
widget list. Leaves a blank space in the grid.
"^"
- Special character used in
grid
widget list. Increasesrowspan
of the widget in the grid directly above it.-column =>
n- Sets the column to place widget in (n >= 0).
-row =>
m- Sets the row to place widget in (m >= 0).
-columnspan =>
n- Sets the number of columns for the widget to span beginning with
-column
.-rowspan =>
m- Sets the number of rows for the widget to span beginning with
-row
.-sticky =>
string- String contains characters
n
,s
,e
, orw
. Widget will stick to those sides.
-in => $otherwindow
- Indicates that widget is gridded inside
$otherwindow
instead of parent of$widget
.-ipadx =>
amount$widget
becomes larger in x direction by 2 ✕ amount.-ipady =>
amount$widget
becomes larger in y direction by 2 ✕ amount.-padx =>
amount- Buffer space equal to amount is placed to left and right of widget.
-pady =>
amount- Buffer space equal to amount is placed on top and bottom of widget.
Explicitly Specifying Rows and Columns
Rather than letting
grid()
make assumptions, it is sometimes necessary to explicitly state the row and column in which the widget should be placed. This is done by using the-row
and-column
options. Each option takes a nonnegative integer as an argument:-column => n, -row => m
When you use
-row
and-column
, it is not necessary to build orgrid()
the widgets in any sort of logical order (except for your own sanity when you are debugging). You could place your first widget in column 10 and row 5 if you like. All of the other cells with lower row and column values will remain empty.Explicitly Spanning Rows and Columns
It is also possible to explicitly indicate that a widget (or widgets) should span some columns or rows. The option to use to span columns is
-columnspan
. For spanning rows, the option is-rowspan
. Both options take an integer that is 1 or greater. The value indicates how many rows or columns should be spanned, including the row or column in which the widget is placed.For this example, I have used the easy way to place widgets in columns and rows by not explicitly specifying the
-row
and-column
options. Note that the secondgrid
command applies to two button widgets, so the single-columnspan
option applies to both buttons created there.$mw->Button(-text => "Button1", -command => sub { exit })->grid
($mw->Button(-text => "Button2", -command => sub { exit }),
$mw->Button(-text => "Button3", -command => sub { exit }),
$mw->Button(-text => "Button4", -command => sub { exit }),
-sticky => "nsew");
# Button5 will span Columns 0-1 and Button6 will span 2-3
$mw->Button(-text => "Button5", -command => sub { exit })->grid
($mw->Button(-text => "Button6", -command => sub { exit }),
-sticky => "nsew", -columnspan => 2);
The resulting window is shown in Figure 2-29.
Figure 2-29. -columnspan example
![]()
This window could also have been created using the
"-"
special character to indicate column spanning, like this:$mw->Button(-text => "Button1", -command => sub { exit })->grid
($mw->Button(-text => "Button2", -command => sub { exit }),
$mw->Button(-text => "Button3", -command => sub { exit }),
$mw->Button(-text => "Button4", -command => sub { exit }),
-sticky => "nsew");
# Button5 will span Columns 0-1 and Button6 will span 2-3
$mw->Button(-text => "Button5", -command => sub { exit })->grid
("-", $mw->Button(-text => "Button6", -command => sub { exit }), "-"
-sticky => "nsew");
This example illustrates how to explicitly use the
-row
and-column
options in addition to the-rowspan
option:$mw->Button(-text => "Button1", -command => sub { exit })->
grid(-row => 0, -column => 0, -rowspan => 2, -sticky => 'nsew');
$mw->Button(-text => "Button2", -command => sub { exit })->
grid(-row => 0, -column => 1);
$mw->Button(-text => "Button3", -command => sub { exit })->
grid(-row => 0, -column => 2);
$mw->Button(-text => "Button4", -command => sub { exit })->
grid(-row => 0, -column => 3);
$mw->Button(-text => "Button5", -command => sub { exit })->
grid(-row => 1, -column => 1);
$mw->Button(-text => "Button6", -command => sub { exit })->
grid(-row => 1, -column => 2);
$mw->Button(-text => "Button7", -command => sub { exit })->
grid(-row => 1, -column => 3);
See Figure 2-30 for the resulting window.
Figure 2-30. Explicit -rowspan example
![]()
Forcing a Widget to Fill the Cell
When you use the
pack()
command, it is necessary to indicate both-fill
and-expand
options to get the widget to resize inside its allocation rectangle. Thegrid()
command doesn't have an allocation rectangle to fill, but it does have the cell within the grid. Using the-sticky
option withgrid()
is similar to using-fill
and-expand
withpack()
.The value associated with
-sticky
is a string containing the compass points to which the widget should "stick." If the widget should always "stick" to the top of the cell, you would use-sticky => "n"
. To force the widget to fill the cell completely, use-sticky => "nsew"
. To make the widget as tall as the cell but only as wide as it needs to be, use-sticky => "ns"
. The string value can contain commas and whitespace, but they will be ignored. These two statements are equivalent:-sticky => "nsew"
-sticky => "n, s, e, w" # Same thing
If you use
-sticky
with your widgets and then resize the window, you'll notice that the widgets don't resize as you think they should. They don't because resizing of the cells and the widgets in them is taken care of with thegridColumnconfigure
andgridRowconfigure
methods, which are discussed later in this chapter.Padding the Widget
grid()
also accepts these four options:-ipadx
,-ipady
,-padx
,-pady
. They work exactly the same as they do inpack()
, but instead of affecting the size of the allocation rectangle, they affect the size of the cell in which the widget is placed.In this example, the
-ipady
and-ipadx
options are applied to the top row of buttons and not the bottom row. Notice in Figure 2-31 how Buttons 5 through 8 are also wider than they really need to be. This is because we used the-sticky => "nsew"
option.$mw->Button(-text => "Button1", -command => sub { exit })->grid
($mw->Button(-text => "Button2", -command => sub { exit }),
$mw->Button(-text => "Button3", -command => sub { exit }),
$mw->Button(-text => "Button4", -command => sub { exit }),
-sticky => "nsew", -ipadx => 10, -ipady => 10);
$mw->Button(-text => "Button5", -command => sub { exit })->grid
($mw->Button(-text => "Button6", -command => sub { exit }),
$mw->Button(-text => "Button7", -command => sub { exit }),
$mw->Button(-text => "Button8", -command => sub { exit }),
-sticky => "nsew");
Figure 2-31. grid -ipadx and -ipady example
![]()
In this example, the
-pady
and-padx
options are applied to the top row of buttons and not the bottom row. Figure 2-32 shows the results.$mw->Button(-text => "Button1", -command => sub { exit })->grid
($mw->Button(-text => "Button2", -command => sub { exit }),
$mw->Button(-text => "Button3", -command => sub { exit }),
$mw->Button(-text => "Button4", -command => sub { exit }),
-sticky => "nsew", -padx => 10, -pady => 10);
$mw->Button(-text => "Button5", -command => sub { exit })->grid
($mw->Button(-text => "Button6", -command => sub { exit }),
$mw->Button(-text => "Button7", -command => sub { exit }),
$mw->Button(-text => "Button8", -command => sub { exit }),
-sticky => "nsew");
Figure 2-32. grid -padx and -pady example
![]()
Specifying a Different Parent
The
-in
option works the same way it does inpack()
. The$widget
will be placed in$otherwindow
and not in the default parent of$widget
. Here is the usage:-in => $otherwindow
Configuring Columns and Rows
As with any of the geometry managers,
grid
has a few methods that are associated with it. Each method is invoked via a widget that has been placed on the screen by usinggrid()
. Sometimes it is necessary to change the options of the group of cells that makes up your grid.You can control resizing and the minimum size of a cell with the
gridColumnconfigure
andgridRowconfigure
methods. Each takes a column or a row number as its first argument and then takes some optional arguments that will change the configuration of that column or row.Both
gridColumnconfigure
andgridRowconfigure
work very similar to theconfigure
method used with widgets. Unlike theconfigure
method used with widgets, however, the options you can specify withgridColumnconfigure
andgridRowconfigure
cannot be used with thegrid()
command. The options you can use withgridColumnconfigure
andgridRowconfigure
are-weight
,-minsize
, and-pad
.If you send only a row or column number, an array is returned with the current options and their values for that method:
@column_configs = $mw->gridColumnconfigure(0);
@row_configs = $mw->gridRowconfigure(0);
In this example, we are getting the options and their values for the first column and the first row. The results of using the default values would look like this:
-minsize 0 -pad 0 -weight 0
-minsize 0 -pad 0 -weight 0
You can get the value of only one of the options by sending that option as the second argument:
print $mw->gridColumnconfigure(0, -weight), "\n";
print $mw->gridRowconfigure(0, -weight), "\n";
The results would be:
0
0
To change the value of the options, use the option and then the value you want associated with it immediately after the option; for example:
$mw->gridColumnconfigure(0, -weight => 1);
$mw->gridRowconfigure(0, -weight => 1);
You can also specify multiple options in one call:
$mw->gridColumnconfigure(0, -weight => 1, -pad => 10);
$mw->gridRowconfigure(0, -weight => 1, -pad => 10);
Now that we know how to call
gridColumnconfigure
andgridRowconfigure
, we need to know what the three different options do.Weight
The
-weight
option sets how much space is to be allocated to that column or row when the window is divided into cells. Remember to use-sticky => "nsew"
in yourgrid()
command if you want the widget to resize when the cell does. The default-weight
is 0, which causes the column width or row height to be dictated by the largest widget in the column. Each-weight
value has a relationship to the other-weight
s in the rows or columns.If a column or row has a
-weight
of 2, it is twice as big as a column or row that has a-weight
of 1. Columns or rows of-weight
0 don't get resized at all. If you want all your widgets to resize in proportion to the size of the window, add this to your code before you callMainLoop
:($columns, $rows) = $mw->gridSize();
for ($i = 0; $i < $columns; $i++) {
$mw->gridColumnconfigure($i, -weight => 1);
}
for ($i = 0; $i < $rows; $i++) {
$mw->gridRowconfigure($i, -weight => 1);
}
This code will assign the
-weight
of 1 to every single row and column in the grid, no matter what size the grid is. Of course, this method only works if you want to assign the same size to each row and each column, but you get the idea.Here is an example of how the
-weight
option works (Figure 2-33 shows the result):$mw->Button(-text => "Button1", -command => sub { exit })->grid
($mw->Button(-text => "Button2", -command => sub { exit }),
$mw->Button(-text => "Button3", -command => sub { exit }),
$mw->Button(-text => "Button4", -command => sub { exit }),
-sticky => "nsew");
$mw->Button(-text => "Button5", -command => sub { exit })->grid
("x",
$mw->Button(-text => "Button7", -command => sub { exit }),
$mw->Button(-text => "Button8", -command => sub { exit }),
-sticky => "nsew");
$mw->gridColumnconfigure(1, -weight => 1);
$mw->gridRowconfigure(1, -weight => 1);
By giving row 1 and column 1 a weight of 1 (whereas all other rows and columns have 0 weight), they take over any extra available space when the size of the window is increased. Notice that columns 0, 2, and 3 are only as wide as is necessary to draw the buttons and their text, but column 1 has filled in the extra space. The same effect happens for row 0 with a weight of 0 and row 1 with a new weight of 1. (The window has been resized larger to demonstrate the effects of
-weight
.)
Figure 2-33. gridRowconfigure and gridColumnconfigure example
![]()
Minimum cell size
The option
-minsize
sets the smallest width for the column or the smallest height for each row. The -minsize option takes a valid screen distance as a value. In this example, the minimum size of the cells in row 0 and column 0 is set to 10 pixels:$mw->gridColumnconfigure(0, -minsize => 10);
$mw->gridRowconfigure(0, -minsize => 10);
If the column or row was normally less than 10 pixels wide, then it would be forced to be at least that large.
Padding
You can add padding around the widget and to the widget by using the
-padx/y
and-ipadx/y
options. You can also add a similar type of padding by using the-pad
option with thegridColumnconfigure
andgridRowconfigure
methods. The padding is added around the widget, not to the widget itself. When you callgridColumnconfigure
, the-pad
option will add padding to the left and right of the widget. CallinggridRowconfigure
with-pad
will add padding to the top and bottom of the widget. Here are two examples:$mw->gridColumnconfigure(0, -pad => 10);
$mw->gridRowconfigure(0, -pad => 10);
Bounding box
To find out how large a cell is, you can use the
gridBbox
method:($xoffset, $yoffset, $width, $height) = $master->gridBbox(0, 2);
This example gets the bounding box for column 0 and row 2. All the values returned are in pixels. The bounding box will change as you resize the window. The four values returned represent the x offset, the y offset, the cell width, and the cell height (offsets are relative to the window or frame where the widget is gridded).
Removing a Widget
Like
packForget
,gridForget
causes the widget(s) to be removed from view on the screen. This may or may not cause the window to resize itself; it depends on the size of$widget
and where it was on the window. Here are some examples:$mw->gridForget(); # Nothing happens
$widget->gridForget(); # $widget goes away
$widget->gridForget($widget1); # $widget and $widget1 go away
$widget->gridForget($w1, $w3); # $widget, $w1, $w3 go away
The widgets are undrawn from the screen, but the cells they occupied remain.
Getting Information
The
gridInfo
method returns information about the$widget
in a list format. Just as withpackInfo
, the first two elements indicate where the widget was placed:@list = $widget->gridInfo();
Here are some sample results from
gridInfo
:-in Tk::Frame=HASH(0x81abc44) -column 0 -row 0 -columnspan 1 -rowspan 2 -ipadx 0 -ipady 0 -padx 0 -pady 0 -sticky nesw
Widget Location
The
gridLocation
method returns the column and row of the widget nearest the given (x, y) coordinates:($column, $row) = $master->gridLocation($x, $y);
Both
$x
and$y
are in screen units relative to the master window (in our examples,$mw
). For locations above or to the left of the grid, -1 is returned.When given the arguments (0, 0), our application returned this:
0 0
which indicates the cell at column 0 and row 0.
Propagation
There is a
gridPropagate
method that is similar topackPropagate
:$master->gridPropagate( 0 );
When given a false value,
gridPropagate
turns off geometry propagation, meaning size information is not sent upward to the parent of$master
. By default, propagation is turned on. IfgridPropagate
is not given an argument, the current value is returned.How Many Columns and Rows?
To find out how large the grid has become after placing numerous widgets in it, you can use
gridSize
to get back the number of columns and the number of rows:($columns, $rows) = $master->gridSize();
The list returned contains the number of columns and then the number of rows. In many of the earlier examples, we had a grid size that was four columns by two rows.
($c, $r) = $f->gridSize(); #$c = 4, $r = 2
It is not necessary for a widget to be placed in a column/row for it to be considered a valid column/row. If you place a widget in column 4 and row 5 using -row=>5, -column=>4 and the only other widget is in row 0 and column 0, then
gridSize
will return 5 and 6.Grid Slaves
There are two ways to find out which widgets have been put in a window or frame. Use
gridSlaves
without any arguments to get the full list or specify a row and column. Here are examples of both:@slaves = $mw->gridSlaves();
print "@slaves\n";
The preceding code would have printed this:
Tk::Button=HASH(0x81b6fb8) Tk::Button=HASH(0x81ba454) Tk::Button=HASH(0x81ba4cc) Tk::Button=HASH(0x81ba538) Tk::Button=HASH(0x81b6fa0) Tk::Button=HASH(0x81ba5e0) Tk::Button=HASH(0x81ba6dc) Tk::Button=HASH(0x81ba748)
We could have specified the widget in column 0, row 0:
$widget = $mw->gridSlaves( -row => 0, -column => 0 );
print "$widget\n";
# Would print this: Tk::Button=HASH(0x81b6fb8)
If you specify only the
-row
option, you'll get a list containing only the widgets in that row. The same goes for only specifying a-column
; your list will contain only the widgets in that column.Place
The
place()
geometry manager is different thangrid()
orpack()
. 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 useplace()
to overlap portions of widgets, which isn't allowed in eithergrid()
orpack()
.Invoking
place()
is similar to calling the other geometry managers:$widget->place( [ option => value, . . . ] );
The options specified when you call
place()
affect how the widgets are put on the screen.Place Options
-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'
- Sets the position in the widget that will be placed at the specified coordinates.
-bordermode => 'inside' | 'outside' | 'ignore'
- Determines whether or not the border portion of the widget is included in the coordinate system.
-height =>
amount- Sets the absolute height of the widget.
-in => $window
- Indicates that the child widget will be packed inside
$window
instead of in the parent that created it. Any relative coordinates or sizes will still refer to the parent.-relheight =>
ratio- Indicates that the height of the widget relates to the parent widget's height by ratio.
-relwidth =>
ratio- Indicates that the width of the widget relates to the parent widget's width by ratio.
-relx =>
xratio- Indicates that the widget will be placed relative to its parent by xratio.
-rely =>
yratio- Indicates that the widget will be placed relative to its parent by yratio.
-width =>
amount- Indicates that the width of the widget will be amount.
-x =>
x- Indicates that the widget will be placed at x. x is any valid screen distance.
-y =>
y- Indicates that the widget will be placed at y. y is any valid screen distance.
Absolute Coordinates
The parent window (or frame) has a standard coordinate system where 0,0 is in the upper-left corner. The x values increase to the right, and the y values increase as you go down. See Figure 2-34.
Figure 2-34. Coordinate system of parent window when absolute coordinates are used
![]()
To use absolute coordinates to specify where to place the widget, we would use options
-x
and-y
:-x => x, -y => y
Valid values for x and y are valid screen distances (for example,
5
, which is in pixels). The widget will have its anchor position (controlled by-anchor
) placed at the x and y coordinates. The default anchor is"nw"
; the upper-left corner of the widget.Another major difference between
place()
and the other geometry managers is that at least two arguments are required whenplace()
is invoked. There are no default values for the-x
and-y
options. You will get an error if you try to invoke place() with no arguments (for example,$widget->place()
).The simplest example of using
-x
and-y
is to place a widget at 0,0:$mw->Button(-text => "Exit",
-command => sub { exit })->place(-x => 0, -y => 0);
As you would expect, the widget ends up in the upper-left corner of the window as shown in Figure 2-35. No matter what size the window, our widget will remain positioned at (0,0). Even when the window is resized as small as possible, the widget will not move.
Figure 2-35. Button placed using -x => 0, -y => 0
![]()
Here is an example of using
-x
and-y
to create some overlapping widgets:$mw->Button(-text => "Exit",
-command => sub { exit })->place(-x => 10, -y => 10);
$mw->Button(-text => "Exit",
-command => sub { exit })->place(-x => 20, -y => 20);
Figure 2-36 shows the resulting window.
Figure 2-36. Overlapping buttons with place()
![]()
Relative Coordinates
In
place()
, there is an additional coordinate system defined for the parent widget that allows relative placement within it. This coordinate system is shown in Figure 2-37.
Figure 2-37. The relative coordinate system
![]()
The upper-left corner has the coordinates (0.0,0.0). The lower-right corner's coordinates are (1.0, 1.0). The middle of the window would be (0.5, 0.5). The coordinates are specified in floating point form to allow
place()
to handle any size window. This allows the widget to remain at that position (in the center, for instance) no matter how the window is resized.It is valid to specify coordinates both smaller than 0.0 and larger than 1.0. However, your widget most likely won't be completely visible in the window when you use out-of-range coordinates.
This code snippet produces the button shown in Figure 2-38:
$b = $mw->Button(-text => "Exit", -command => sub { exit });
$b->place(-relx => 0.5, -rely => 0.5);
Figure 2-38. Using place with -relx => 0.5, -rely => 0.5
![]()
Although the button in Figure 2-38 is placed in the middle of the screen, it looks off-center because the upper-left corner of the widget was placed in the middle of the window instead of the center. You can change this with the
-anchor
option, which I'll discuss shortly. If we resize this window, the button still stays in the middle of the window (see Figure 2-39).
Figure 2-39. -relx => 0.5, -rely => 0.5 window resized larger
![]()
This next example creates two buttons, both placed in the window with relative coordinates:
$mw->Button(-text => "Exit",
-command => sub { exit })->place(-relx => 0.2,
-rely => 0.2);
$mw->Button(-text => "Exit",
-command => sub { exit })->place(-relx => 0.5,
-rely => 0.5);
No matter what size the window is or where other widgets are in the screen, the two buttons will stay in those relative locations (see Figure 2-40).
Figure 2-40. Two buttons placed relative to the parent window
![]()
The left window in Figure 2-40 is the default size of the window when it was created. The right window is what it looks like after the window was resized to make it much smaller. Notice that the second button placed in the window remains on top. It does so because we are still maintaining the ordered list of widgets in the window; the second Exit button (placed at 0.5,0.5) is drawn last, so it's drawn above the other button.
You can also combine the absolute and relative coordinate systems simply by using both in the argument list. The relative coordinate system is considered first, and then the x or y value is added to that position. The options
-relx => 0.5, -x => -10
means to place the widget 10 pixels to the left of the middle of the window.Anchoring the Widget
Think of the child widget as a piece of paper that you want to put on your bulletin board (the board is the parent widget). You have a tack that you are going to use to keep the paper up on the board. You can put the tack right through the center of the paper, in the upper-left corner (
"nw"
), or in the lower-right corner ("se"
). The point where the tack is going to stick the paper to the board is the-anchor
point. The -anchor point on the widget is "tacked" to the coordinates given by-x
,-y
or-relx
,-rely
. The default-anchor
is"nw"
. Figure 2-37 shows these-anchor
points within the child widget.It is important to know where the
-anchor
is because it will affect how we see the widget within the parent.
Figure 2-41. Different -anchor values affect where the widget is placed in the window
![]()
In Figure 2-41, almost identical place commands were used to put the Exit button in the window, but the
-anchor
value was changed. The left window's button was created with this command:$mw->Button(-text => "Exit",
-command => sub { exit })->place(-relx => 0.5,
-rely => 0.5);
The window on the right in Figure 2-41 used this command:
$mw->Button(-text => "Exit",
-command => sub { exit })->place(-relx => 0.5,
-anchor => "center",
-rely => 0.5);
As with
pack()
andgrid()
, the possible values for-anchor
are:'n'
,'e'
,'s'
,'w'
,'center'
,'nw'
,'sw'
,'ne'
, and'se'
. However, the value now applies to the child widget instead of the position within the allocation rectangle.Width and Height
When you use
place()
, you can specify the width and height of the widget in one of three ways:
- Allow the widget to determine its own size.
- Specify width and/or height in absolute measurements.
- Specify width and/or height in relative measurements (relative to the parent widget).
To let the widgets determine their own size, no options are specified at all. The other ways involve the options
-width
,-height
and-relwidth
,-relheight
respectively.The
-width
and-height
options allow you to specify the exact width or height of the widget in a screen distance:-width => amount, -height => amount
Each amount is a valid screen distance (discussed earlier in this chapter under pack). The widget will be these sizes even if it cuts off edges of the items displayed in it. Our button looks quite silly on the screen when we use a
-width
of 40 pixels (see Figure 2-42).$mw->Button(-text => "This Button Will Cause the Program to Exit",
-command => sub { exit })->place(-x => 0, -y => 0,
-width => 40);
Figure 2-42. Using -width with place()
![]()
The other two options,
-relwidth
and-relheight
, determine the widget in relation to the parent widget.-relwidth => ratio, -relheight => ratio
The ratio is a floating point number (similar to that specified by
-relx
or-rely
). A value of 1.0 will make the widget as wide (or as tall) as the parent widget. A value of 0.5 will make the widget half as wide as the parent (see Figure 2-43).
Figure 2-43. Example of the same window resized with -relwidth => 0.5, -relheight => 0.5
![]()
The options
-width
and-relwidth
are additive when used together, and so are-height
and-relheight
.Border Options
Normally, the border of the widget is used as the edge of the possible space in the window, which means any widgets placed with either the absolute or relative coordinate system will be placed inside the border. This can be changed by using the
-bordermode
option:-bordermode => 'inside' | 'outside' | 'ignore'
Using
'outside'
will allow the coordinate system to use the space occupied by the border as well. A value of'ignore'
will have the coordinate system use the space designated as the official X area. Overall, this option is pretty useless, as you can see from the difference each makes on our example in Figure 2-44.
Figure 2-44. -bordermode examples
![]()
If you look very closely (get out your magnifying glass), you can see that the
'outside'
version is two pixels higher and two pixels to the left than the'inside'
version. This is because on my window manager (fvwm), my border is defined as 2 pixels.Methods Associated with Place
The methods for
place()
are simple and don't allow much manipulation of the widgets.Removing the widget
As with
pack
andgrid
, there is aplace
version of theForget
method:$widget->placeForget();
If you use this method on the widget, the widget will be removed from view on the screen. It is also removed from the list maintained by the parent widget.
Place information
The
placeInfo
method returns a list of information related to the widget:@info = $widget->placeInfo();
print "@info";
## Produced these results (there are blanks where there are no values)
-x 0 -relx 0 -y 0 -rely 0 -width -relwidth -height -relheight -anchor nw
Place slaves
@widgets = $parent->placeSlaves();
placeSlaves
returns a list of the slave widgets that are within$parent
. The list looks the same as it does when it is returned frompackSlaves()
orgridSlaves()
.xGeometry Management Summary
You now know more about the three different geometry managers than you'll ever need to know to write a successful Perl/Tk application. Here are some helpful hints on deciding which geometry manager to use:
pack()
is good for general purpose use and will be your choice around 95% of the time.grid()
is perfect for those situations in which you would like to create a columnar layout that is similar to a spreadsheet.place()
is most useful when you want your widgets to stay in a position or size that is relative to the widget that created them. When it is used correctly, it can be very powerful.- No matter which manager you use, take the time to get the widgets on your window where they belong (or more likely, where you want them). There's nothing more unsettling than a button that looks like it just doesn't belong in the window.
As you read through this book, you'll notice that some of the option names for the geometry managers are also option names when you are creating or configuring a widget type. For example, you can specify the
-width
of a button without usingplace()
. Always keep in mind the context in which the option is used. Sometimes the functional difference is very subtle.
1. Several people have told me that
pack
can also take a list of widgets. I didn't cover this because it is not howpack
is normally used.2. When I used the special character
"^"
with Tk4.002, I got a nasty core dump. This is fixed in Tk8.0, so if you get this error also, check which version you have.
© 2001, O'Reilly & Associates, Inc.