12—
Frames
A frame widget is a boring widget at first glance. All it does is provide a place for other widgets to sit. This doesn't seem important, but it is. The geometry managers provided with Perl/Tk have some limitations (see Chapter 2, Geometry Management), and we can use frames to help them do their jobs better. We'll use pack as our example geometry manager throughout this chapter because it seems to be the most popular, but remember that the basic rules for using a frame apply to the other geometry managers as well.
A frame widget's job is to contain other widgets, accommodating the size of the widgets within. If you don't have any widgets packed into the frame, you won't see the frame. If the widgets inside the frame are resized for any reason, the frame will try to resize as well (either larger or smaller).*
Creating a Frame
Use the parent widget of the frame to invoke the Frame() method:
$frame = $parent->Frame( [ option => value, ... ])->pack();
The $parent can be a MainWindow, a toplevel, or another frame widget.** After the frame is created, it can become a parent to other widgets. You must have created the frame but not necessarily packed it on the screen for it to be the parent
* You can change this behavior by using packPropagate() or gridPropagate().
** Technically, any widget can be a parent of another widget, but I like to make my life easier when it comes to placing the widget inside the window. If I made a frame the child of a $button, I wouldn't be able to pack it inside the button. I would then have to use the -in option with pack, confusing myself even further. Keep it simple, and you'll be much happier.
of other widgets. Keep in mind that, even if you pack other widgets inside your frame, if you don't pack the frame as well, the other widgets won't show on the screen.
Just as with all the other widgets in Perl/Tk, the options specified in the Frame method will change how the frame looks inside the window. There are few options available with the frame widget, and they aren't complicated at all. This section covers all the options and what they do.
-background => color
Sets the color of the frame's background area (there is no foreground area).
-borderwidth => amount
Sets the width of the frame's edges. Default is 0.
-class => classname
Indicates the class associated with the frame in the option database. This option can actually be used on any widget, not just a frame.
-colormap => "new" | $window
Specifies whether to use a new colormap or share one with another widget in the application. Default is undef.
-container => 0 | 1
Tk8.0 only. If true, this frame will be used to contain another embedded application.
-cursor => cursorname
Changes the cursor to use when the mouse pointer is over the frame.
-height => amount
Sets the starting height of the frame in a valid screen distance.
-highlightbackground => color
Sets the color the highlight rectangle should be when the frame does not have keyboard focus.
-highlightcolor => color
Sets the color the highlight rectangle should be when the frame does have the keyboard focus. Default color is black.
-highlightthickness => amount
Sets the thickness of the highlight rectangle. Default is 0.
-label => labelstring
Adds a label to the Frame with the text "labelstring".
-labelPack => [ pack options ]
Specifies pack options for the label.
-labelVariable => \$variable
Specifies a variable that contains the text for the label.
-relief => 'flat' | 'groove' | 'raised" | 'ridge' | 'sunken' | 'solid'
Changes the appearance of the edges of the widget. Default is 'flat'.
-takefocus => 0 | 1 | undef
Specifies whether the frame should take the focus. Default is 0.
-visual => "type #"
When used on an X Windows system, changes the depth of colors available to your application. Does nothing on Win32 systems.
-width => amount
Sets the starting width of the frame in a valid screen distance.
Frame Style
As with all widgets, you can use the -relief and -borderwidth options to change how the edges of a frame widget are drawn. The default -relief is 'flat', and the default -borderwidth is 0. If you want the frame to have any edges at all, make sure you change -borderwidth to something higher than 0. Unless you put something in a frame, you'll never see it. So, for the examples in Figure 12-1, I have inserted a label widget and an entry widget that state the relief of that frame. Note that I actually created a label widget by using Label() and did not use the -label option (see the next section).
0253-01.gif
Figure 12-1.
Different relief values for frames; borderwidth of 2 and borderwidth of 5
Using -relief and -borderwidth is a great way to find out where your frame is in the window. If you have a complicated window, it's confusing to remember which frame is where. I'll often add -borderwidth => 5, -relief => "groove" to my Frame command to find that frame in the window.
Adding a Label to a Frame
With Perl/Tk. you can add a label to your frame by using the -label option, which takes a text string as an argument:
$mw->Frame(-label => "My Frame:")->pack;
...
# configure label in frame later :
$frame->configure(-label => "My Frame:")->pack;
By default, the label is placed at the top of the frame, centered across the width (see Figure 12-2). Again, I put something in the frame so you can see the frame as well as the item in the frame. In this case, I placed a button with the default pack options in the frame. I also created the frame with -relief => 'groove', -borderwidth => 2 options so you can see the edge.
0254-01.gif
Figure 12-2.
Frame with label in default position
You can change the location of the label inside the frame by using the -labelPack option. It takes an anonymous array as an argument, where the array contains any pack options for the label:
-labelPack => [ -side => 'left', -anchor => 'w']
Be careful to notice that this option has an uppercase letter in it. If you try to use -labelPack without the capital "P," you'll get a compilation error. Also notice that there isn't a -labelGrid option available. You must use pack() to put widgets inside your frame if you are going to use the -label option. If you don't, bad things happen (your application might not run at all).
Instead of using a static text string with your frame's label, you can assign a variable by using the -labelVariable option (again, notice the capital V):
-labelVariable => \$label_text
When you change the contents of the variable $label_text, the label in the frame will change as well.
The instant you use the -label or -labelVariable option, a label is created and placed inside the frame. You can use these options either in the initial Frame() call or later with $frame->configure( ... ). If you use them later, the label is placed above all other widgets inside the frame.
Frames Aren't Interactive
The frame widget itself is not interactive; by default, it can't accept input from the user. The widgets inside it can, but the frame cannot. As always, the focus ability is controlled by the -takefocus option:
-takefocus => 0
With a frame widget, it is set to 0. If for some reason you need to get input from the user on your frame, you will need to change it to -takefocus => 1.
Colormap Complications
When you are running several applications at once and you start a web browser, you'll sometimes notice that the colors become corrupted. When you switch from an application to the browser, the colors in your other applications suddenly change. If you switch back from your browser to an application, the browser colors change. This is happening because the web browser is a color hog. It has requested more colors than the operating system can allocate at once. The OS must alter the colormap between applications to allow the active application to use the colors it wants to use. The colormap simply gives the operating system a way to keep track of who is using which colors.
Perl/Tk applications can have many colors too-you can get color-happy and make each button a different color of the rainbow. This can cause problems if there are other applications running that want a lot of different colors too. If other applications are color hogs, Perl/Tk will switch to black-and-white mode. If you don't like this behavior, you can use the -colormap option to override it. -colormap takes either the word "new" or a reference to another window. If given "new", it will create its own colormap. When you use -colormap with another window, the two windows will share the colormap. But there is one catch, and that is the -visual option.
The -visual option takes as an argument a string that contains a keyword and a number; for example:
-visual => "staticgrey 2"
The keyword can be any one of the following: staticgrey, greyscale, static- color, pseudocolor, truecolor, or directcolor. The number indicates the depth of color used (2 = black/white).
When you use -colormap to share the colormap between two windows, the -visual option for both must be the same. This means that -visual must be undef for both (the default) or it must have the same value. Neither -colormap nor -visual can be altered by using the configure method.
You will see both -colormap and -visual options in Chapter 13, Toplevel Widgets, also. We covered it here first because this is where we see it first. To be honest, you'll probably never use either option in either widget.
The Magical Class Option
You can force your frame to be in another class (besides frame) by using the -class option. Simply give it a string that is a unique class identifier;
-class => "Myframe"
For more information on using classes (and what good they can do you), see Chapter 13.
Frame Methods
The only methods available with the frame widget are cget and configure. These are described in detail in Appendix A, Configuring Widgets with configure and cget.
Fun Things to Try
When you use the Scrolled method, you are using frames without even knowing it. The newly scrolled widget is placed inside a frame with its scrollbars so that it behaves as one contained widget. Here are some other ways you can use frames:
• Create several lines of labels with entry widgets. Each 'line' needs to be in its own frame so it will look right.
• Place an image along one side of your application window. Put the widgets in a frame (on the left or on the right) and place the image on the other side of them.
• Place a scrolled listbox in your application window, a frame containing three buttons (OK, Cancel, Apply) along the bottom of the window, and a frame along the right containing two buttons (Delete, Add) that manipulate the listbox. By using frames, you can keep the buttons that belong together in one area instead of grouping them with other buttons that serve a different purpose.