4—
Checkbuttons and Radiobuttons
This chapter discusses both checkbutton and radiobutton widgets. Although they are very similar, they are used for different purposes.
Checkbuttons are useful when you want to select as many items as you want, such as a shopping list. Radiobuttons are used in group situations when you must make a choice between items, such as on a multiple-choice exam:
Q1: What year did Columbus discover America?
A) 1400
B) 1470
C) 1472
D) 1492
E) none of the above
Because radiobuttons are grouped together, you are forced to select one and only one choice in that group. If the default choice is always A and you click on D, A would be automatically unclicked (or unselected).
The two sections in this chapter cover how to use both widget types and ways to set them up and configure them.
The Checkbutton Widget
0081-01.gif
In Chapter 3, The Basic Button, you learned the options associated with the button widget. A checkbutton is also considered a type of button (and it uses many of the same options), even though the way it is used in an application is different from the way a standard button is used.
Instead of clicking on a checkbutton and expecting something to happen immediately, you use it to indicate a yes or no answer. If the checkbutton is checked it
means yes; unchecked means no. You might use a checkbutton to list options for printing a document. The text on the checkbuttons might say Print Header Page, Even Pages Only, Odd Pages Only, and Number Pages. At the bottom of the window there would be a Print button, and when you clicked on it, the program would find out which checkbuttons were selected and submit the print job accordingly.
A window listing several jobs to run (like a batch job controller) might use checkbuttons to ask the user if each job should be run. If the checkbutton next to the job name is selected, the job will be run. If the checkbutton is unselected, then the job will be skipped this time around.
Each time a checkbutton is used, the application is asking the user to answer a yes or no question. Checkbuttons that make up a group are typically related (as in our print job example), but they don't have to be because the answer to each checkbutton is independent of any other widgets or checkbuttons on the screen.
The checkbutton is similar to a button; it displays a text string, but it also has an indicator on the left side of the widget. By default, the outside edges of the checkbutton don't have 3D relief like a standard button does, but the indicator (that little square on the left) does.
A checkbutton operates in the same way a standard button does; you click on it with the left mouse button. A button will change its own -relief (the way the edges of the button are drawn) to look like it has been pushed down whereas a checkbutton will only change the state of the indicator. If the checkbutton is on, the indicator will look as if it has been pushed into the window and filled with a darker color.* If the checkbutton is off, it will look like a tiny gray button.
Sometimes the terminology becomes confusing because there is the indicator's status (or value) and the state of the checkbutton itself. If the checkbutton looks like a tiny raised button without color, then it is off (Figure 4-1; left checkbutton). If it is filled in with color, we say it is on (Figure 4-1; right checkbutton). The state of the entire checkbutton (including the indicator) can be normal, active, or disabled. Both checkbuttons in Figure 4-1 have a state of normal.
0082-01.gif
Figure 4-1.
A checkbutton that is off and one that is on
* Some operating systems actually put a checkmark tick.gif into the little box. Others use a tiny ''x" in the indicator to show the state as on.
As when creating any widget, the checkbutton is created using a method named after the capitalized version of the widget name, Checkbutton, invoked from the parent widget. The basic usage looks like this:
$cb = $parentwidget->Checkbutton( [ option => value, ... ] )->pack;
In addition to having an indicator with a status, the checkbutton also can have a callback that uses the -command option associated with it. When the checkbutton is clicked (regardless of the indicator's status), the callback is invoked. However, it isn't always necessary to associate a callback for radiobuttons and checkbuttons since you can just check the status of the radiobutton or checkbutton later on in the program.
The boolean status of the checkbutton is stored in a variable that you give via the -variable option when it is created. Each checkbutton should have its own status stored in its own unique variable. When the checkbutton is clicked, the status is updated. In addition, any callback associated with the -command option is invoked (regardless of the new status of the checkbutton). The options that change a checkbutton's behavior are listed below and explained in greater detail afterwards.
Checkbutton Options
The following checkbutton options work exactly the same as a standard button, so I won't go over them in detail again. Refer to Chapter 3, for complete descriptions of these options: -activebackground, -activeforeground, -anchor, -background, -borderwidth, -cursor, -disabledforeground, -font, -foreground, -height, -highlightbackground, -highlightcolor, -highlightthickness, -justify, -padx, -pady, -state, -takefocus, -text, -textvariable, -underline, -width, and -wraplength.
The rest of the options behave a little differently or are exclusive to the checkbutton widget. They are covered in the following list. Some options deal with only the indicator (such as -selectimage). Remember that the -state option refers to the entire checkbutton widget, and the status of the indicator is governed by the options -onvalue, -offvalue, -indicatoron, and -variable.
-activebackground => color
Sets the color the widget's background should be when the mouse is over it.
-activeforeground => color
Sets the color the widget's text should be when the mouse is over it.
-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'
Sets the position of the text within the widget. Most noticeable when the widget is resized larger.
-background => color
Sets the color of the widget background (behind the text).
-bitmap => bitmap
Displays this bitmap instead of text.
-borderwidth => amount
Sets the edge thickness of the widget. Also changes the thickness of the indicator. Default is 2.
-command => callback
Associates a subroutine to the button. Called when button is clicked.
-cursor => cursorname
Sets the cursor to change to cursorname when it is over the widget.
-disabledforeground => color
Sets the color of the text when
-state is 'disabled'.
-font => fontname
Sets the font to use when displaying text in the widget.
-foreground => color
Sets the color of the text.
-height => amount
Sets the height of the button; amount is a valid screen distance.
-highlightbackground => color
Sets the color the highlight rectangle around the widget should be when the widget does not have focus.
-highlightcolor => color
Sets the color the highlight rectangle around the window should be when the widget does have focus.
-highlightthickness => amount
Sets the thickness of the highlight rectangle.
-image => imgptr
Displays image instead of text.
-indicatoron => 0 | 1
Determines whether to display the indicator.
-justify => 'left' | 'right' | 'center'
Sets the justification of the text within the widget.
-offvalue => newvalue
Sets the value used when the button is off. Must be a scalar. Default is 0.
-onvalue => newvalue
Sets the value used when the button is on. Must be a scalar. Default is 1.
-padx => amount
Sets the amount of space left between text/indicator and left/right edges of widget.
-pady => amount
Sets the amount of space left between text/indicator and top/bottom edges of widget.
-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'
Changes the look of the widget edges.
-selectcolor => color
Sets the color of the indicator when on.
-selectimage => imgptr
Indicates the image to display instead of text when button is on. Ignored if
-image is not used.
-state => 'normal' | 'disabled' | 'active'
Sets the state of the widget. If disabled, it will not respond to any input.
-takefocus => 0 | 1 | undef
Determines if the widget is available for focus or not.
-text => "text"
Sets the text displayed in the widget.
-textvariable => \$variable
Indicates that text in $variable is displayed as text in widget.
-underline => n
Underlines the nth character in the text string.
-variable => \$value
Associates the on/off value of indicator with $variable.
-width => amount
Sets the widget to this width. Can be any valid screen distance.
-wraplength => amount
Indicates that the text will wrap when it exceeds this amount.
Storing the Indicator's Status
The option -variable will associate a variable with the status of the indicator by sending a reference as the value. To use the scalar $value, you would add this to the option list of the Checkbutton call:
-variable => \$value
Just as the -textvariable option sets the variable that is associated with the text of the checkbutton, this option sets the variable associated with the indicator.
When the checkbutton is clicked, $value will now contain the status of the indicator (the value placed in $value is defined by -onvalue and -offvalue and by default are 1 and 0 respectively).
In addition to using the mouse to change the status, you can also change the contents of $value directly. If your code contains $value = 1 at some point, the indicator will be turned on. You can specify $value = 1 before creating the checkbutton, which will draw the checkbutton for the first time with the indicator on. If you change the value in $value at any time after you create the checkbutton in your program, the checkbutton will change to reflect the new value. The subroutine associated with -command (if there is one) is not invoked when the value of $value is changed.
Utilizing -variable is usually the easiest way to check the status of the indicator on the button. Here is an example that has two buttons that change the value in $cb_value:
$cb_value = 0;
$cb = $mw->Checkbutton(-text => "Checkbutton",
                       -variable => \$cb_value,
                       -command => sub { print "Clicked! $cb_value\n" }
                       )->pack(-side => 'top');

$mw->Button(-text => "CB on",
            -command => sub { $cb_value = 1 })->pack(-side => 'left');
$mw->Button(-text => "CB off",
            -command => sub { $cb_value = 0 })->pack(-side => 'left');
See Figure 4-2 for the resulting window.
0086-01.gif
Figure 4-2.
Buttons changing the value of a checkbutton
The value stored in $cb_value can be changed in three ways: clicking on the checkbutton, clicking on the "CB off" button, or clicking on the "CB on" button. Only when you click on the checkbutton will you see the word "Clicked!" written in the shell window from which it was run.
There are other ways to change the value associated with the checkbutton. See invoke, select, deselect, and toggle in "Checkbutton Methods" later in this chapter.
Assigning a Callback
The -command option works just like it does for a standard button, but usually the function associated with a checkbutton's callback does something less obvious. Many times, there is no callback at all associated with the checkbutton because the important information is the status of the checkbutton rather than whether the checkbutton was just clicked.
One of the things a checkbutton might do is alter the appearance of the window. The checkbutton might look something like the one in Figure 4-3.
0087-01.gif
Figure 4-3.
Checkbutton that will display other widgets on the screen when clicked
When the user clicks on the checkbutton to turn it on, our window magically changes to look like Figure 4-4.
0087-02.gif
Figure 4-4.
Window after clicking the checkbutton
Here's the code that makes the magic happen:
#!/usr/bin/perl -w
use Tk;
$mw = MainWindow->new;
$mw->title("Checkbutton")

## Create other widgets, but don't pack them yet!
for ($i = 1; $i <= 5; $i++) {
  push (@buttons, $mw->Button(-text => "Button$i"));
}

$mw->Checkbutton(-text => "Show all widgets"
                 -variable => \$cb_value,
                 -command => sub {
                   if ($cb_value) {
                     foreach (@buttons) {
                       $_->pack(-side => 'left');
                     }
                   } else {
                     foreach (@buttons) {
 
                     $_->pack('forget');
                   }
                 }
                })->pack(-side => 'top');
MainLoop;
So we can display some widgets later on in the program, we create them ahead of time and store references to them in the @buttons array. The buttons in this example aren't very useful because they don't even have a -command associated with them. Normally, they would each have a specific task they would perform when pressed; however, for our example, we just want them to exist.
Then we create our magic checkbutton. When the button is clicked (regardless of the status of its indicator), it will call the subroutine pointed to by -command. Our subroutine looks at the current value of $cb_value, shows the buttons if it is on, and hides them if it is off. The value in $cb_value is changed before this subroutine is called. When our checkbutton is clicked again, the extra buttons will be removed from the window and the window will shrink back to the size it was previously.
This type of setup is great when you want to keep a basic window uncluttered but want the ability to show more widgets if the user can handle the advanced functions of the extra widgets. For example, you can create a Find window that has a place to enter some text, a button to start the find, and an Advanced Search checkbutton. Clicking on Advanced Search would add some more widgets to the bottom of the window allowing you to match case, use regular expressions, and use other fancy search mechanisms.
On and Off Values
If you don't like the default on value of 1, you can use the -onvalue option to change it:
-onvalue => newvalue  ## Default is 1
The same is true of the off value:
-offvalue => newvalue ## Default is 0
These options will change the values stored in $variable. Depending on how you would like the checkbutton to interact with the rest of your application, sometimes it makes sense to use different values. The newvalue could be anything, as long as it is a scalar value. This means that you can use references to arrays and hashes if you really want to.
It is good practice to keep the meaning of -onvalue the opposite of -offvalue. If -onvalue is now the string "ON", logically -offvalue should be "OFF". Of
course, if the purpose of this checkbutton is to use a more accurate value of p, then -onvalue could be "3.14159265359", and -offvalue could be "3.14".
Be careful when you use unusual values for -onvalue and -offvalue. If you set the variable to something that doesn't equal either one of them, the checkbutton will be considered off, even though the value of the $variable will not equal the -offvalue value. For instance, if you set -onvalue => 1, -offvalue => 0, and you set $variable to 3, then the checkbutton will be considered off.
Indicator Color
You can use the -selectcolor option to alter the color that fills in the indicator when the checkbutton is selected:
-selectcolor => color
The default value is "#b03060" (a dark pink color). Changing the value for -selectcolor will also change the background of the button when the button is selected and -indicatoron => 0.
Hiding the Indicator
One of the ways a checkbutton is different from a standard button is the indicator. Use the -indicatoron option to tell Perl/Tk not to draw that funny little square button at all:
-indicatoron => 0 | 1
As we have seen from the previous examples, the default for -indicatoron is 1 (i.e., show the indicator). If we change this -indicatoron to 0, the checkbutton will look almost like a normal button (without quite as much space around the text, though). Even though it looks a lot like a regular button, its behavior when it is clicked (to turn the indicator, which is hidden, to on) is completely different (see Figure 4-5). Note that the -relief option is ignored completely when -indicatoron is set to 0.
0089-01.gif
Figure 4-5.
Checkbutton with -indicatoron => 0. Window on left is unchecked. Window on right is checked.
In this example, the color for the background on the checked button is the -selectcolor, not the -backgroundcolor. You might want to use the nonindicator configuration if you change the text of the button to reflect the new state of the checkbutton. For instance, change Logging Enabled to Logging Disabled.
Displaying a Picture Instead of Text
As you can with a normal button, you can use the -image option to display an image in place of the text on a checkbutton. Another option, -selectimage, is available to display a different image when the checkbutton has been clicked:
-image => $imgptr [ , -selectimage => $imgptr ]
The usage statement shows -selectimage as optional because it will be ignored if -image is not used.
The imgptrs can be created by using the same methods as those used in Chapter 3 with a button's -image option: $arrow = $mw->Photo(-file => "nextart.gif");
The image will be put in place of the text on the checkbutton. These options have precedence over the -text option, so if both -text and -image are listed, the -text option will be ignored.
Which image is displayed depends on whether the button is on or off. If only the -image option is specified, this image will be displayed no matter what. If -selectimage is also specified, the image associated with it will be displayed when the button is checked. Figure 4-6 shows an example that uses both options.
0090-01.gif
Figure 4-6.
The same window with the checkbutton unchecked (on the left) and checked (on the right)
The checkbuttons in Figure 4-6 were created with this code snippet:
$img1 = $mw->Bitmap(-file =>
                    "/usr/X11R6/include/X11/bitmaps/lineOp.xbm");
$img2 = $mw->Bitmap(-file => "/usr/X11R6/include/X11/bitmaps/xlogo32")

$mw->Checkbutton(-text => "Checkbutton",
                 -image => $img1,
                 -selectimage => $img2,
                 -variable => \$cb_value)->pack(-side => 'top');
Using two different images to indicate whether the checkbutton is off or on might make more sense if you also use -indicatoron and set it to 0. For instance, if you want to indicate that a document is locked (read-only) or unlocked you could use a picture of a lock for -image and a picture of a lock with a line through it for -selectimage.
For this example, I chose to use bitmap files as our images. Instead of using the -image option to display a bitmap, you could use the -bitmap option directly. The -bitmap option is exactly the same as the standard button -bitmap option; it replaces the text of the button with the specified bitmap (see Figure 4-7).
0091-01.gif
Figure 4-7.
Checkbutton with -bitmap => 'warning'
Unlike -image and -selectimage, using -bitmap will not change the image when the button is clicked on or off.
You might think that using images instead of text would make your application easier for non-English speakers to understand. However, if you use too many checkbuttons with images, you might confuse people even more. A few easily understood icons are better than a large collection of vague icons.
Checkbutton Style
Although the button and checkbutton both share the -relief and -borderwidth options, and they mean the exact same thing, when they are used with a checkbutton, the effects are visually different because of the indicator. As a reminder, the possible values are:
-relief => 'flat'|'groove'|'raised'|'ridge'|'sunken'|'solid'
-borderwidth => amount
Figure 4-8 shows the different relief types when a default -borderwidth value is used. The default for a checkbutton is 'flat' because the relief of the outside edge of the checkbutton doesn't change when the checkbutton is clicked; only the indicator changes. Figure 4-8 also shows that the edges of the checkbutton are much closer to the text; the -padx and -pady default values are smaller than the default for a button. The -relief option does not affect the indicator.
The -borderwidth option affects both the outside edge of the checkbutton and the indicator inside the checkbutton. The indicator itself stays the same size no
0092-01.gif
Figure 4-8.
Example of all possible -relief types
matter what the borderwidth of the widget is, but the indicator's edges change in width. When you use a large -borderwidth, you get some interesting results, as shown in Figure 4-9.
0092-02.gif
Figure 4-9.
Example using -borderwidth => 4
We used a -borderwidth of 4, and you can see that the outside edges got a bit thicker, and so did the edges of the indicator. With a larger -borderwidth, there is much less room to show the indicator's color when it is on (see the tiny square in the middle of those indicators?).
0092-03.gif
Figure 4-10.
Example using -borderwidth => 10
In Figure 4-10, we used a -borderwidth of 10. Notice the remarkable difference! We can no longer see the indicator at all, even though there is still space left for it. When these checkbuttons are checked or unchecked, there is no way to tell what the current state it is because the indicator is essentially invisible.
I highly recommend that you do not use the -borderwidth option associated with a checkbutton because of this interesting side effect.
Configuring a Checkbutton
Like the button widget, the checkbutton has methods that can manipulate it after it is created. These methods can be invoked at any time after the checkbutton is created, even before it is displayed on the screen.
You can use both configure and cget methods with a checkbutton as well. These methods are explained in Appendix A, Configuring Widgets with configure and cget.
Turning a Checkbutton On and Off
You can force the checkbutton to go from on to off or vice versa using the deselect and select methods.
The deselect method will always set the indicator to the off state and the variable assigned by -variable to the value in -offvalue:
$cb->deselect();
The opposite of deselect, select will cause the indicator to be set to the on state and the variable assigned by -variable to the -onvalue:
$cb->select();
Both methods are ignored if -state is 'disabled'.
You can also toggle the indicator from on to off or vice versa using the toggle method:
$cb->toggle();
Calling toggle does not cause the subroutine associated with the -command value to be called.
Flashing the Checkbutton
You can make the indicator flash with the -background and -foreground colors by calling flash:
$cb->flash();
Invoking the Checkbutton
To perform the same action as clicking the checkbutton with the left mouse button, call invoke:
$cb->invoke();
It will cause any callback associated with -command to be called; it will also switch the state of the indicator from on to off or vice versa.
The Radiobutton Widget
0093-01.gifA radiobutton looks similar to a checkbutton because it also has an indicator on the left side. The radiobutton indicator is a diamond, rather than a square. Both look 3D and are slightly raised when unselected.
The main difference between a radiobutton and a checkbutton is the function they serve in an application. A radiobutton is used to select one of several different choices:
• In a multiple choice test, the answers A, B, C, D, or E
• Which version of a tool you would like to use
• Your income range: 0-20,000; 20,001-30,000; 30,001-40,000; 40,000 and up
• Which type of entree you prefer: beef, chicken, or vegetarian
In each example, only one answer is appropriate. For instance, it wouldn't make sense to have a salary of both $18,000 and $33,000. And when you are taking a multiple-choice test, you can't select all the answers and hope that the teacher gives you credit. You have to pick only one.
Because radiobuttons are used to decide between several choices, you should always create at least two.* It doesn't make sense to ask a question at all if there is only one choice. Radiobuttons should always be created in groups of two or more.
Creating Radiobuttons
So far, you have learned to create one widget at a time. Because radiobuttons are always in a group, we generally need to create more than one at a time. So you can be efficient and create all the widgets as quickly and painlessly as possible, I'll show you some quick ways to make up a group of radiobuttons.
(To show you how to take advantage of the widgets in the best way, the examples will start to get a bit more complicated. By now, you should know the basics of widget creation and how to specify options during the creation. I'll often say that an option works exactly like it did with widget X and refer you to that chapter for a more complete discussion.)
Radiobuttons are similar to checkbuttons; they also have a $variable associated with the state of the indicator (using the option -variable). When you create a group of radiobuttons, use the same $variable for every radiobutton in the group. The value put into the $variable will change according to which radiobutton is selected. To create a new group of radiobuttons, simply associate the new group with a different $variable
Because each radiobutton in a group points to the same $variable, there isn't a concept of onvalue and offvalue. The offvalue would be whatever the other radiobutton wanted it to be. To accomplish this, radiobuttons use the option -value instead of -onvalue and -offvalue.
* If you did create only one radiobutton, it would start out unselected (unless the variable you associated with it contained the on value). Once that radiobutton was selected, you would never be able to deselect it.
For our first example, we'll create a group of radiobuttons that indicate the background color of our window. We need to use colors that are valid to the $mw-> configure(-background => color) command. Simple color names usually work, so we will use red, yellow, green, blue, and gray.
As always, the basic usage for creating a radiobutton is as follows:
$rb = $parentwidget->Radiobutton( [ option => value, ... ] )->pack;
Here is the code that will create the radiobutton group that controls the background color:
# setup the default value we would like
$rb_value = "red";
$mw->configure(-background => $rb_value);

# create the radiobuttons that will let us change it
foreach (qw(red yellow green blue grey)) {
  $mw->Radiobutton(-text => $_,
                   -value => $_,
                   -variable => \$rb_value,
                   -command => \&set_bg)->pack(-side => 'left');
}

# function to change the background color using $rb_value
sub set_bg {
  print "Background value is now: $rb_value\n";
  $mw->configure(-background => $rb_value);
}
We are storing the status of our radiobutton group in $rb_value. We set it to an initial value of "red", which happens to match the first radiobutton we are creating. When any of the radiobuttons are clicked, including the one currently selected, the subroutine set_bg will be called. This subroutine will print the new value of $rb_value and then change the background of our main window to that color.
One thing to note: Although we set the default value of our radiobutton group to "red", that doesn't mean that the background of the window has been set to red as well. We do this by calling the configure command and sending it the value in $rb_value. We could also do it by an explicit call to the set_bg routine, or we could have done it when we created the MainWindow.
The window we have created looks like Figure 4-11.
0095-01.gif
Figure 4-11.
Radiobuttons that will change the background color of the window
The best way to understand how this window works is to type in the code and run it. This will be true for a lot of the examples shown in this book. When you click on each radiobutton, you'll see a strip of the window at the top and bottom change color. You'll only see this small strip because we only changed the background of $mw, not of each radiobutton or the exit button.
Now that we've seen a basic application of radiobuttons, we can go over each of the options.
Radiobutton Options
As with the checkbutton, the following options are the same for any of the three types of buttons: -activebackground, -activeforeground, -anchor, -background, -borderwidth, -cursor, -disabledforeground, -font, -foreground, -height, -highlightbackground, -highlightcolor, -highlightthickness, -padx, -pady, -state, -takefocus, -text, -textvariable, -underline, and -width.
In addition, the following options are the same between checkbutton and radiobutton: -command, -indicatoron, -image, -selectimage, -bitmap, -wraplength, -justify, and -selectcolor.
The rest I will discuss because they behave a bit differently because of the context in which we are using them.
-activebackground => color
Sets the color the widget's background should be when mouse is over it.
-activeforeground => color
Sets the color the widget's text should be when the mouse is over it.
-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'
Sets the position of the text within the widget. Most noticeable when the widget is resized larger.
-background => color
Sets the color of the widget background (behind the text).
-bitmap => bitmapname
Displays this bitmap instead of text.
-borderwidth => amount
Sets the edge thickness of the widget. Also changes the thickness of the indicator. Default is 2.
-command => callback
Associates a subroutine to the button. Called when the button is clicked.
-cursor => cursorname
Indicates that the cursor will change to cursorname when it is over the widget.
-disabledforeground => color
Sets the color of the text when
-state is 'disabled'.
-font => fontname
Sets the font to use when displaying text in the widget.
-foreground => color
Sets the color of the text.
-height => amount
Sets the height of the button; amount is a valid screen distance.
-highlightbackground => color
Sets the color the highlight rectangle around the widget should be when the widget does not have focus.
-highlightcolor => color
Sets the color the highlight rectangle around the window should be when the widget does have focus.
-highlightthickness => amount
Sets the thickness of the highlight rectangle.
-image => imgptr
Indicates that an image is displayed instead of text.
-indicatoron => 0 | 1
Indicates the status of the indicator; 0 means indicator is not displayed.
-justify => 'center' | 'left' | 'right'
Sets the justification of the text within the widget.
-padx => amount
Sets the amount of space left between text/indicator and left/right edges of the widget.
-pady => amount
Sets the amount of space left between text/indicator and top/bottom edges of widget.
-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'
Changes the look of the widget edges.
-selectcolor => color
Sets the color the indicator should be when on.
-selectimage => imgptr
Indicates that an image should be displayed instead of text when the button is on. Ignored if
-image is not used.
-state => 'normal' | 'active' | 'disabled'
Sets the state of the widget. If disabled, it will not respond to any input.
-takefocus => 0 | 1 | undef
Determines whether the widget is available for focus or not.
-text => textstring
Sets the text displayed in the widget.
-textvariable => \$variable
Indicates that the text in $variable is displayed as text in the widget.
-underline => n
Underlines the nth character in the text string.
-value => newvalue
Sets the value assigned to
$variable (set with -variable option) when this radiobutton is selected (default is 1).
-variable => \$variable
Sets the variable to use when this radiobutton is clicked.
-width => amount
Sets the width of the widget. Can be any valid screen distance.
-wraplength => amount
Indicates that the text will wrap when  it exceeds this amount.
Using the -variable Option
The -variable option will look the same in each radiobutton creation command, except logically, we are using it differently. We should have several radiobuttons sharing the same $variable instead of each one having their own distinct $variable. The example for Figure 4-11 shows how this works.
Setting the Value
With checkbuttons, we had two options, -onvalue and -offvalue, because we had to worry about the state of each individual checkbutton. With radiobuttons, we only care about the state of the whole group. Each radiobutton should have a different -value to it, so a glance at $variable will tell us which radiobutton is selected.
The default -value is 1. (Remember: When you use a group that consists of only one radiobutton, that one is always checked.)
To make sure you understand the difference between the -variable option and the -value option, let's walk through a short example.
$mw->Radiobutton(-text => "Beef", -value => "Beef",
                 -variable => \$entree);
$mw->Radiobutton(-text => "Chicken", -value => "Chicken",
                 -variable => \$entree);
$mw->Radiobutton(-text => "Vegetarian", -value => "Vegetarian",
                 -variable => \$entree);
Here we have created three radiobuttons that all use the variable $entree to store their values in. If the user selects Beef, then $entree will contain the value of "Beef". If the user selects Chicken, then $entree will contain the value of "Chicken". Later in the program when we build the physical menu for the printer, we can just check to see what is in $entree to find out what that user wants for dinner.
Radiobutton Style
Okay, so the -relief option does the same thing it does in a checkbutton. But it is worthwhile to show a screen shot of what happens when different relief types are used (see Figure 4-12).
0099-01.gif
Figure 4-12.
Different relief types for a radiobutton
As with the checkbutton, changing -borderwidth can cause the radiobutton to look drastically different (see Figure 4-13).
0099-02.gif
Figure 4-13.
Radiobuttons with -borderwidth of 4
Remember how the indicator completely disappeared in a checkbutton when we used a -borderwidth of 10? Well, in a radiobutton, it makes it look kind of like a kite (see Figure 4-14). You still won't be able to tell which radiobutton is checked or not, so I don't recommend using the -borderwidth option.
0100-01.gif
Figure 4-14.
Radiobuttons with -borderwidth of 10
Configuring a Radiobutton
Just as you can with our other widgets, you can use configure and cget to get or set option values for each radiobutton widget. See Appendix A for more details on how to use these methods.
Selecting and Unselecting a Radiobutton
A radiobutton also has both select and deselect methods:
$rb->deselect();
$rb->select();
Using select causes the radiobutton to be selected. (Using deselect will cause the radiobutton to be unselected. It sets the $variable to an empty string. If you use this method, make sure you account for it in any code that evaluates the value of $variable). Any command associated via the -command option will also be invoked with both select and deselect.
Flashing the Radiobutton
The flash method will flash the radiobutton's background/foreground colors off and on, but otherwise, it does nothing interesting:
$rb->flash();
Invoking a Radiobutton
To programmatically select a radiobutton, use the invoke method:
$rb->invoke();
It causes the radiobutton to be selected and will also invoke any callback associated with the radiobutton via the -command option. Essentially, it does the same thing it would do if you clicked on the radiobutton with the mouse.
Fun Things to Try
• Create a bunch of checkbuttons and a Go button that will report the status of all the checkbuttons.
• Make up a survey that uses checkbuttons for questions that have one or more options and radiobuttons with only one appropriate choice.
• Create three different groups of checkbuttons: Favorite Color, Favorite Song, and Shoe Size. Then create a radiobutton to represent each group. The currently selected radiobutton dictates which checkbuttons the user can see and use.
5—
Label and Entry Widgets
There are times you'll want users to type in specific information such as their name, address, or even a serial number. The simplest way to do this is to use entry widgets. You can use a label widget with an entry to clearly communicate to the user what should be typed in the entry. Most often, you'll see the label and entry combination used multiple times in a database entry-type window where there are many different pieces of information the user must enter.
The Label Widget
0102-01.gifSo far, all we have talked about are buttons, buttons, and more buttons. What if we just want to put some informative text on the screen? The label widget does just that. A label is like a button that doesn't do anything. It is a noninteractive widget and by default cannot have the keyboard focus (meaning you can't tab to it) and it does nothing when you click on it.
The label widget is probably the simplest widget. It is similar to a button in that it can show text (or a bitmap), have relief (default is flat), display multiple lines of text, have a different font, and so on. Figure 5-1 shows a simple window, with both a button and label, created with this code:
use Tk;
$mw = MainWindow->new();
$mw->Label(-text => "Label Widget")->pack();
$mw->Button(-text => "Exit", -command => sub { exit })->pack();
MainLoop;
0103-01.gif
Figure 5-1.
A simple window with label and button
Here are some typical uses for a label:
• Put a label to the left of an entry widget so the user knows what type of data is expected.
• Put a label above a group of radiobuttons, making their purpose more clear (e.g., ''Background Color:"). You can do the same thing with checkbuttons if they happen to be related or along the same theme.
• Use a label to tell users what they did wrong: "The number entered must be between 10 and 100." (Typically, you would use a Dialog composite widget to give messages to the user like this, but not always.)
• Put an informational line across the bottom of your window. All the other widgets would have a mapping that displays a string containing information about that widget.
Creating a Label
The command to create a label is, of course, Label. Here's the basic usage:
$label = $parent->Label( [ option => value ... ] )->pack();
Hopefully, you are starting to see a trend in the creation command. As you might expect, when you create a label, you can specify options that will change its appearance and how it behaves.
Label Options
The following list is a comprehensive list of options for labels:
-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'
Causes the text to stick to that position in the label widget. This won't be obvious unless the label is forced to be larger than standard size.
-background => color
Sets the background color of the label to color.
-bitmap => bitmap
Displays the bitmap contained in bitmap instead of text.
-borderwidth => amount
Changes the width of the edges of the label.
cursor => cursorname
Changes the cursor to cursorname when the mouse is over this widget.
-font => fontname
Indicates that the text in the widget will be displayed with fontname.
-foreground => color
Changes the text of the button (or the bitmap) to be color color.
-height => amount
Sets the height of the label to amount; amount is a valid screen distance.
-highlightbackground => color
Sets the color of the focus rectangle when the widget is not in focus to color.
-highlightcolor => color
Sets the color of the focus rectangle when the widget has focus to color.
-highlightthickness => amount
Sets the width of the focus rectangle. Default is 0 for the label.
-image => imgptr
Displays the image to which imgptr points instead of text.
-justify => 'left' | 'right' | 'center'
Sets the side of the label against which multi-line text will justify.
-padx => amount
Adds extra space inside the edge to the left and right of the label.
-pady => amount
Adds extra space inside the edge to the top and bottom of the label.
-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken'
Changes the type of edges drawn around the button.
-takefocus => 0 | 1 | undef
Changes the ability of the label to have the focus or not.
-text => text
Displays in the label a text string.
-textvariable => \$variable
Points to the variable containing text to be displayed in the label. Label will change automatically as $variable changes.
-underline => n
Causes the nth character to be underlined. Allows that key to invoke the widget when it has the focus. Default value is -1 (no character underlined).
-width => amount
Causes the label to be width amount.
-wraplength => amount
Indicates that the text in the label will wrap when it gets longer than amount.
This list briefly describes each option and what it does. Some of the options have different defaults for the label widget than we are used to seeing with the buttontype widgets, causing the label to behave a bit differently.
How a Label Differs from Other Widgets
When we created button-type widgets, we could either click them with the mouse or tab to them and then use the keyboard to cause the button to be pressed. A label widget, on the other hand, does not interact with the user. It is there for informational purposes only, so there is no -command option. We also can't tab to a label widget because nothing would happen.
The default value for the -takefocus option is 0, making the label noninteractive. When tabbing between widgets on the screen, the highlight rectangle shows us which widget currently has the keyboard focus. Since we don't allow the label to have the focus (remember, -takefocus is set to 0), it doesn't make sense to have a visible highlight rectangle. The default value for the -highlightthickness option in a label widget is 0. You can make a rectangle appear around a label by setting -highlightthickness to something greater than 0, and setting -highlightbackground to a color such as blue or red.
The label widget also doesn't have a -state option. Since we shouldn't be able to click a label, we should never have to disable it.
Relief
In Figure 5-2, you can see what happens when you change the label's -relief option. Notice that the edges of the widget are very close to the text. Unlike a button, you usually don't want much extra space around the label (space is controlled by the -padx and -pady options). Normally, you want the label widget to sit right next to the widget (or widgets) it is describing.
0105-01.gif
Figure 5-2.
Labels with different relief values. Window on right has a -borderwidth of 10.
You'll notice that I like seeing what widgets look like with the different relief values. This sometimes helps determine where the widget ends, especially with widgets that
have a default value of "flat". Also, I often change the relief of different widgets to make sure I know which widgets are where on the screen. After creating 10 entries and labels with less than creative variable names, it's easy to lose track. Also, changing the borderwidth is bound to make that one widget stand out. Of course, I always change the relief and borderwidth back to something non-obnoxious before I give the program to anyone else to run! Color is also a good way to do a diagnostic message.
Status Message Example
I often use the groove or ridge relief when I'm making a help or status label along the bottom of my window. I make a label that is packed with -side => 'bottom' and -fill => 'x'. There are two different ways you can use a status label:
• Set the variable associated with it so it changes as your program progresses, announcing to the user that it is busy, or something is happening.
• Have the help label give information on each of the different widgets in your application when it gets the focus, using the bind command.
Both types are demonstrated in the following sample code.
This code shows the "What I'm doing now" type of help label:
$mw->Label(-textvariable => \$message, -borderwidth => 2,
           -relief => 'groove')->pack(-fill => 'x',
                                      -side => 'bottom');
$mw->Text()->pack(-side => 'top',
                            -expand => 1,
                            -fill => 'both');

$message = "Loading file index.html...";
...
$message = "Done";
The label is created across the bottom of the screen. We pack it first because we want it to stay on the screen if we resize the window (remember, the last widgets packed will get lower priority if the window runs out of room). As the program executes (represented by the...), it changes the label accordingly.
This code shows an example of using a widget-helper help label:
$mw->title("Help Label Example");

$mw->Label(-textvariable => \$message)
   ->pack(-side => 'bottom', -fill => 'x');

$b = $mw->Button(-text => "Exit", -command => \&exit)
        ->pack(-side => 'left');
&bind_message($b, "Press to quit the application");
 
$b2 = $mw->Button(-text => "Do Nothing")->pack(-side => 'left');
&bind_message($b2, "This button does absolutely nothing!");

$b3 = $mw->Button(-text => "Something",
  -command => sub { print "something\n"; })->pack(-side => 'left');
&bind_message($b3, "Prints the text 'something'");

sub bind_message {
  my ($widget, $msg) = @_;
  $widget->bind('<Enter>', [ sub { $message = $_[1]; }, $msg ]);
  $widget->bind('<Leave>', sub { $message = ""; });
}
This example is a bit longer because we are using the bind method (the bind method is explained in more detail in Chapter 14, Binding Events). For each widget we create, we want to associate a help message with it. We do this by adding bindings to each widget that change the variable $message to a specified string when the mouse enters the widget, and to an empty string if the mouse leaves the widget. We used a subroutine to avoid writing the same two bind lines over and over again. Figure 5-3 shows what our window looks like with the mouse over the center button.
0107-01.gif
Figure 5-3.
Window with label across the bottom
Container Frames
In Figure 5-3, you can see that the example text is centered within the label widget. When using non-multiple line labels, when you fill the widget across the screen, the text remains centered, even if you add the -justify => 'left' option. You can get around this by creating a container frame, giving it the desired relief, filling the frame across the screen (instead of the label), and placing the label widget within the frame:
$f: = $mw->Frame(-relief => 'groove',
 
                -bd => 2)->pack(-side => 'bottom',
                                -fill => 'x');
$f->Label(-textvariable => \$message,)->pack(-side => 'left');
This allows the label to grow and shrink within the frame as necessary, while the text sticks to the left side. If you've typed this short little example in and played with the strings bound to each widget, you might have noticed that the window will resize itself if the text assigned to $message is too long to display in the label. This can get annoying if your window is fairly small to begin with. There are two ways to deal with this: First, you can always use really short text strings, and second, you can tell the window to not resize when the label changes size.
The drawbacks with each approach aren't too bad, and which one you pick just depends on the application you are working on. If you can make really short sentences that make sense, great. Telling the window to not resize is almost as easy, though-it is accomplished by adding one line to your program:
$mw->packPropagate(0);
Using packPropagate will cause your window to not resize when a widget is placed inside the window (we first talked about packPropagate in Chapter 2, Geometry Management). This means that your window might not be showing all your widgets right away. You can deal with this by keeping it on until you get all your widgets in it, figuring out a good starting size for your window and using $mw->geometry(size) to request that size initially. (See Chapter 13, Toplevel Widgets, for info on the geometry method.)
Label Configuration
Label is a pretty boring widget, so there are only two methods available to change or get information on it: cget and configure. Both methods work for Label the same way they work for the Button widget. Please refer to Appendix A for the details on arguments and return values.
The Entry Widget
0108-01.gifUntil now, the only input we know how to get from the user is a mouseclick on a button widget (Button, Checkbutton, or Radiobutton), which is handled via the -command option. Getting input from a mouseclick is useful, but it's also limiting. The entry widget will let the user type in text that can then be used in any way by the application. Here are a few examples of where you might use an entry widget:
• In a database form that requires one entry per field (e.g., Name, Last name, Address)
• In a software registration window that requires a serial number
• In a login window that requires a username and password
• In a configuration window to get the name of a printer
• In an Open File window that requires the path and name of a file
Normally, we don't care what users type in an entry widget until they are done typing, and any processing will happen "after the fact" when a user clicks some sort of Go button. You could get fancy and process each character as it's typed by setting up a complicated bind-but it is probably more trouble than it is worth.
The user can type anything into an entry widget. It is up to the programmer to decide if the text entered is valid or not. When preparing to use the information from an entry, we should do some error checking. If we want an integer and get some alphabetic characters, we should issue a warning or error message to the user.
An entry widget is a much more complex widget than it first appears to be. The entry widget is really a simplified one-line text editor. Text can be typed in, selected with the mouse, deleted, and added. I would classify an entry widget as a middle-of-the-line widget. It's more complicated than a button, but much less complicated than the text or canvas widgets.
Creating the Entry Widget
No surprises here:
$entry = $parent->Entry( [ option => value ... ])->pack;
When the entry widget is created, it is initially empty of any text, and the insert cursor (if the entry had the keyboard focus) is at the far-left side.
Entry Options
The following list contains a short description of each option available for configuring an entry widget. Several of them are discussed in more detail later in this chapter.
-background => color
Sets the background color of the entry widget. This is the area behind the text.
-borderwidth => amount
Changes the width of the outside edge of the widget. Default value is 2.
-cursor => cursorname
Changes the cursor to cursorname when it is over the widget.
-exportselection => 0 | 1
If the Boolean value specified is true, any text selected will be exported to the windowing system's clipboard.
Fun Things to Try
• Create a bunch of checkbuttons and a Go button that will report the status of all the checkbuttons.
• Make up a survey that uses checkbuttons for questions that have one or more options and radiobuttons with only one appropriate choice.
• Create three different groups of checkbuttons: Favorite Color, Favorite Song, and Shoe Size. Then create a radiobutton to represent each group. The currently selected radiobutton dictates which checkbuttons the user can see and use.