3—
The Basic Button
The Button Widget
0057-01.gif Of all of the widgets available with Perl/Tk, the button is one of the most commonly used. Just see all the examples in Chapter 2, Geometry Management. When the button is pressed, something happens. That something can vary from exiting the program (as in our Hello World example) to beginning a longer series of operations such as opening a file or starting another process. The button typically displays a short text string: Done, Apply, Save, Ok, Exit.
Other widgets are also classified as buttons: radiobuttons, checkbuttons, and menubuttons. This chapter covers the traditional button. Chapter 4, Checkbuttons and Radiobuttons, and Chapter 11, Menus, will cover the other types because they look different on the screen and behave differently as well.
We cover the button widget first because it is easy to see the way different options affect it on the screen. Many of the other widgets included in Perl/Tk utilize the same options. Usually, if the option name is the same, the change to the widget will also be the same (more or less).
Creating a Button
The basic usage to create a button is as follows:
$button = $parentwidget->Button( [ option => value, ... ] );
We have already seen examples of buttons in our Hello World program in Chapter 1, and in all the geometry management examples in Chapter 2. In these examples, a button is created and placed on the screen with a command like this:
$mw->Button(-text => "Done",-command => sub { exit })->pack;
You can save a reference to the button like this:
$button = $mw->Button(-text => "Done",-command => sub { exit })->pack;
In Hello World, we didn't need to refer to the button again later, so it wouldn't have made sense to save a reference to it. But most of the examples for this chapter will assume that we've saved a reference to the button widget when we created it.
Button Options
The rest of this chapter covers the options available to change the look of the button and how to make it do what you want.
-activebackground => color
Sets the color the background should be when the mouse cursor is over the button. A color is a text string such as
"red".
-activeforeground => color
Sets the color the text should be when the mouse cursor is over the button.
-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'
Causes the text to stick to the specified position in the button.
-background => color
Sets the background of the button to color.
-bitmap => 'bitmapname'
Sets default bitmap or the location of a bitmap file (with @ in front of path).
-borderwidth => amount
Changes the width of the edge drawn around the button. (Emphasizes the
-relief.)
-command => callback
Indicates a pointer to a function that will be called when the button is pressed.
-cursor => 'cursorname'
Indicates that the mouse cursor will change to 'cursorname' when over the button.
-disabledforeground => color
Sets the color the text should be when the button is disabled.
-font => 'fontname'
Changes the font of all the text on the button.
-foreground => color
Changes the text color to color.
-height => amount
Sets the height of the button in characters if text is displayed, and the screen distance if an image or bitmap is displayed.
-highlightbackground => color
Sets the color of the area behind the focus rectangle (shows when widget does not have focus).
-highlightcolor => color
Sets the color of the focus rectangle.
-highlightthickness => amount
Sets the thickness of the black box around the button; indicates focus.
-image => $imgptr
$imgptr
is a pointer to an Image object that was made with a GIF or PPM/PGM file.
-justify => 'left' | 'right' | 'center'
Sets the side of the button against which multiline text will justify itself.
-padx => amount
Adds extra space to the left and right side of the button inside the button edge.
-pady => amount
Adds extra space to the top and button inside the button edge.
-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'
Changes the type of edges drawn around the button
-state => 'normal' | 'disabled' | 'active'
Indicates the button's state of responsiveness. If set to "disabled", the button does not respond.
-takefocus => 0 | 1 | undef
Indicates that the button will never get focus (0), always get focus (1), or let the application decide (undef).
-text => 'text'
Sets the text string displayed on the button.
-textvariable => \$variable
Pointer to a variable containing text to be displayed in button. Button text will change as $variable does.
-underline => n
Underlines the nth character in the text string. Allows keyboard input via that character when button has the focus.
-width => amount
Sets the width of the button in characters if text is displayed and as a screen distance if an image or bitmap is displayed.
-wraplength => amount
Sets the screen distance for the maximum amount of text displayed on one line. Default: 0.
Displaying Text
For the user to know what the button does when it's pressed, you need to indicate the function of the button with its text string. The option that does this is -text:
-text => 'text'
When you are trying to come up with a descriptive text string, short and simple is the key. You don't want your button to take over the whole window with a long text string.
The string can be anything: alphanumeric, newline(s), or variables. Just like any other string in Perl, if it is put in single quotes, it is taken literally, and with double quotes, it is interpolated. The interpolation only happens once (the first time the option is parsed). If the variable changes later in the program, it has no effect on the text in the button. The only way the text in the button can be changed after it has been created is by using the configure method to reset it (e.g., $button-> configure (-text => "newtext");) or by using the -textvariable option.
There is no default for the -text option; the button will simply have no text if -text is not specified.
The other way to display text on the button is by using the -textvariable option. The -textvariable option allows a scalar variable to be associated with the button; anything in the variable will be displayed on the button. Specify the scalar variable as follows:
-textvariable => \$variable
This means the text of the button will change as the contents of $variable change. When the text within the button changes, the button may become larger or smaller, and the entire window may change size.
This piece of code shows how the -textvariable option is used:
$count = 0;
$mw->Button(-text => "Add 1",
            -command => sub { $count++ })->pack(-side => 'left');
$mw->Button(-textvariable => \$count)->pack(-side => 'left');
$mw->Button(-text => "Exit",
            -command => sub { exit })->pack(-side => 'left');
Figure 3-1 shows two windows. The first window shows how it looks when it is first created, and the second window shows what it looks like after clicking on the ''Add 1" button many times.
0061-01.gif
Figure 3-1.
Example of using -textvariable
Displaying an Image or Bitmap Instead of Text
Instead of displaying a text string on the button, you can use the -image option to display an image:
-image => $imgptr
GIF and PPM/PGM formats are valid image types. Support is available for JPEG images in a separate module (Tk::JPEG), which is available for download from CPAN. Other types of images are also supported as more modules like Tk::JPEG are being developed. Check CPAN to see what is currently available.
When using an image, only the image will be displayed because the button can display only a text string or an image, not both. To create an $imgptr variable, use the Photo method (and supply the name and path if the image is not in the current directory) of the image file. The $imgptr is passed in as a value to the -image option:
$arrow = $mw->Photo(-file => "Xcamel.gif");
$mw->Button(-text => 'Exit', -command => sub { exit },
            -image => $arrow)->pack;
Figure 3-2 shows an example of a button with a GIF file on it.*
0061-02.gif
Figure 3-2.
Button with image instead of text
Use the -bitmap option to allow a button to display a bitmap specified in a text string:
-bitmap => 'bitmapname'
* When I tried this example under the Windows 95 OS, I didn't get a good colormap of the GIF file. The problem may have been my video card or video driver for Windows 95, so it might look better on your machine.
There are several default bitmaps: error, gray12, gray25, gray50, gray75, hourglass, info, questhead, question, and warning (see Figure 3-3). They are specified in the option by placing single quotes around the bitmap name:
$mw->Button(-bitmap => 'error', -command => \&handle_error)->pack;
To specify a bitmap from a file, you need to put an @ in front of the path.
$mw->Button(-bitmap => '@/usr/nwalsh/mybitmap',
             -command => sub { exit })->pack;
Note that, if you use double quotes, you have to escape the @ with a backslash, (e.g., "\@/usr/nwalsh/mybitmap").
0062-01.gif
Figure 3-3.
Window showing all the default bitmaps
Assigning a Callback
In addition to the -text option, the -command option is almost always used to create a button. For the button to do something when pressed, we have to associate a callback with the button via the -command option. The callback happens when mouse button 1 is released over the button.* If you click down on the button but move the cursor away from the button before releasing, nothing happens because the mouse-click was aborted.
In the Hello World program, we used the exit routine as our callback:
$mw->Button(-text => "Done", -command => sub { exit })->pack;
There are several ways to associate a subroutine or set of commands with the button. This discussion will apply to all widgets that have a -command option, so you will see this option referred to often.
Defining a -command Callback
There are several ways the callback can be defined:
• Anonymous subroutine: e.g., sub { .. do something .. }
• Reference to a subroutine: e.g., \&mysub
• Anonymous list with the first element as a subroutine pointer, and the rest of the list as arguments to the subroutine: [ \&mysub, $arg0, $arg1, \@arg2 ...]
* Mouse button 1 is the leftmost mouse button, mouse button 2 is the middle mouse button, and mouse button 3 is the rightmost mouse button.
The button we created in our Hello World program used an anonymous subroutine. Here is the code again:
$mw->Button(-text => "Done", -command => sub { exit; })->pack;
We also could have created the anonymous subroutine prior to the button creation and sent the reference to it instead:
$mysubref = sub { exit };
$mw->Button(-text => "Done", -command => $mysubref)->pack;
This is useful when our anonymous subroutine does some fancy things and it would look awkward shoved into the list of arguments.
We could also create a regular subroutine to handle the exit and then just pass a reference to it:
sub do_exit {
  &do_something_else;
  exit;
}
$mw->Button(-text => "Done", -command => \&do_exit )->pack;
It is a good idea to use a subroutine like this if you have more than one way to exit the application. For instance, you could set up your application to exit via a menu, a button, or the window manager Close command.
If we need to pass arguments to our do_exit() routine we would use the anonymous list form:
sub do_exit {
  my ($arg1, $arg2) = @_;
  &do_something_else if ($arg1 = 12);
  exit;
}
$mw->Button(-text => "Done",
            -command => [ \&do_exit, $arg1, $arg2 ])->pack;
It is important to remember how the different ways to specify a callback affect the scope and which variables you can access.
Anonymous subroutines
Anonymous subroutines merely get "set aside" to be called later from within MainLoop. The commands inside the anonymous subroutine are not parsed until then. Any variables you use will not be evaluated until that time.
foreach (@names) {
  $mw->Button(-text => $_,
            -command => sub { print "$_ was pressed!\n"; })->pack;
}
In the preceding code, we are using the $_ variable in the foreach loop. The button's text string will be set as expected because the $_ is evaluated when the button is created. However, the $_ that is inside the scope of the anonymous subroutine doesn't get evaluated until the button is actually pressed. At that point, $_ could be undefined and errors start printing out every time you click the button.
Subroutine references: arguments or no arguments
The subroutine reference and the anonymous list are very similar except the list allows additional arguments (especially ones from within the current scope) to be sent to the subroutine:
foreach (@names) {
  $mw->Button(-text => $_,
            -command => [ \&print_name, $_ ])->pack;
}
sub print_name {
  print "$_[0] was pressed!\n";
}
The anonymous list gets created during the call to create the button. This means that $_ in the list is evaluated within the context of the foreach loop and will be set to the same value as the -text option uses.
Those of you who are comfortable with creating anonymous subroutines on the fly can also do it this way:
foreach (@names) {
  $mw->Button(-text => $_,
              -command => [ sub { print "$_[0] was pressed!\n"; },
                            $_ ])->pack;
}
The anonymous subroutine is the first item in the list to the -command option, and the second item in the list is the argument $_. Keep in mind that sometimes you'll want a lot more than a single print statement in the subroutine, and for readability, it makes sense to put it in a named subroutine.
This is meant to be a brief overview of anonymous subroutines as it relates to Perl/Tk. The Camel Book* has all the information you would ever want and more.
Disabling a Button
When a button is created, it shows up on the screen by default, ready for action. The button will change colors when the mouse passes over it and will perform the assigned callback when pushed. You can change this by using the -state option:
-state => "normal" | "disabled" | "active"
* Technically known as Programming Perl, also available from O'Reilly & Associates, Inc.
The "normal" state was just described. The "active" state is when the mouse cursor is physically over the button and is used internally. The "disabled" state is when the button appears grayed out (or with whatever colors have been specified by -disabledforeground and -disabledbackground) and will not respond to the mouse at all.
A button should not be available for selecting unless it makes sense in the application; for example, a button that disables another when it is pressed. The code would look like this:
my $exit_b = $mw->Button(-text => 'Exit',
                         -command => sub { exit })->pack;
$var = "Disable Exit";
$mw->Button(-textvariable => \$var,
            -command => sub { my $state = $exit_b->configure(-state);
                              if ($state eq "disabled") {
                                $exit_b->configure(-state => 'normal');
                                $var = "Disable Exit";
                              } else {
                                $exit_b->configure(-state => 'disabled');
                                $var = "Enable Exit";
                              }})->pack;
In this example, a reference to the Exit button is saved because it needs to be used later to change the state of the button. Also, note that $exit_b is used inside the scope of the anonymous subroutine. This will only work if $exit_b is left in the global scope of the entire program so that $exit_b will be defined when the anonymous subroutine is executed. Be careful to not set $exit_b to something else; if you do, when the anonymous subroutine is invoked, it will refer to the new value in $exit_b, not the one you wanted.
Figure 3-4 shows the window after we have clicked on the Disable Exit button once.
0065-01.gif
Figure 3-4.
Window with disabled button (Exit) and normal button
The configure() method is explained later in this chapter; you don't need to worry about how it works just yet.
By disabling widgets when they can't do anything, you can give users visual hints about what they can and cannot do in the application.
Manipulating the Text
In addition to displaying text in the button, you can alter the appearance and location of the text within the button. The simplest thing you can do is to use the -font option to change the font:
-font => 'fontname'
There are several ways to specify the font. If you are using Tk4 (which is the current version as the book is being written) you should follow the directions in the following paragraphs. If you are using the newest version of Perl/Tk, which includes Tk8.0,* you should see Appendix C, Fonts; it covers the new methods to use with fonts.
The new font is specified as a text string that contains a font name. There is a difference in the way you specify fonts for Win32 systems and Unix systems. Valid fonts for your Unix system can be obtained by using the xlsfonts command or by using the Tk::Fonts module. The default font for the button widget on my Unix system is**:
"-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
Although you'll see this default on a Win32 system, you need to send a different type of string as the value to the -font option. Here's an example:
-font => "{Times New Roman} 12 {normal}"
You can look in the Control Panel under Fonts to see which fonts are available. Double-clicking on them will bring up a window that shows the font in the different sizes available. Use the name of the font as it's listed in the Fonts directory for the first part of the font name between the curly braces. The number after the font name is the size of the font in points. The third part is the type of the font, usually normal, italic, or bold.
There can only be a single font for each button, so the text string cannot change font in the middle of a word. Each button (or widget) in an application can have a different font. Here is an example of two buttons in a window, one with the default font and the other with "lucidasans-14" (a Unix font) as its font:
$mw->Button(-text => "Exit",
            -command => sub { exit })->pack(-side => 'left',
                                      -fill => 'both', -expand => 1);
$mw->Button(-text => "Exit",
            -font => "lucidasans-14",
            -command => sub { exit })->pack(-side => 'left',
                                      -fill => 'both', -expand => 1);
Figure 3-5 shows the resulting window.
* The numbering system for Perl/Tk follows the Tcl/Tk version numbers. I have no idea why they skipped 5, 6, and 7.
** Not all fonts are available on every system, although your system's default should work. Use the following command to get the default font for your system: @config = $button->configure(-font); print "@config\n";
0067-01.gif
Figure 3-5.
Buttons with various fonts
In addition to changing the font, you can also move the text around within the button. As you can in a word processing document, you can change where the text will adjust itself. The option that controls this is -justify:
-justify => 'left' | 'right' | 'center'
The default for -justify is 'center'. Normally, the text displayed in a button is a quick one- or two-word statement; for example, Exit, Done, Yes, No, or Cancel. The justification of the text isn't too obvious unless multiple lines of text are used. By default, the button will only display multiple lines if a \n is included in the string. You can have the program help decide when to wrap by using the -wraplength option:
-wraplength => amount
The amount indicates the maximum length of the line as a valid screen distance (see Chapter 1). If the length of the text string in the button exceeds this amount, the text will be wrapped around to the next line. The default for -wraplength is 0.
This is an example that uses both the -justify and -wraplength options:
foreach (qw(left center right)) {
  $b =  $mw->Button(-text =>"This button will be justified $_",
                    -command => sub { exit },
                    -wraplength => 53,
                    -justify => $_)->pack(-side => 'left',
                                          -fill => 'both',
                                          -expand => 1);
  }
Figure 3-6 shows the results of the three buttons. Although this example doesn't show it, it is possible for the text to be wrapped in the middle of a word.
0067-02.gif
Figure 3-6.
Effects of -justify and -wraplength in a button
The final possible adjustment to the text (or bitmap) is its position within the button. This is controlled by the -anchor option, which is similar to the -anchor option used with the different geometry managers:
-anchor => 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'center'
Like the window, the button has compass points that define locations within the button. Figure 3-7 shows where these points are in the button.
0068-01.gif
Figure 3-7.
Anchor points within a button
The default position for the text is 'center'. When the position is changed, it is not obvious that this option is in effect unless the button is resized larger. In Figure 3-8, the button is the same one that was created in the -justify example (Figure 3-6) except -anchor => 'nw' has been added to the option list.
0068-02.gif
Figure 3-8.
Anchor on button set to 'nw'
As mentioned earlier, this option is similar to the -anchor option to the pack command. It is important to note that this option changes the position of the text in the button; the -anchor option to pack() changes the position of the widget in the window.
Altering the Button's Style
By default, a button looks like it's slightly raised off the surface of the window. By using the -relief option, you can change the style of the button edges:
-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'
Each value changes the look of the button slightly, as you can see in Figure 3-9.
flat
Makes it look like only text is present in the window. 'flat' is not recommended for a button because the user has no visual information that the button can be pressed (the button looks just like a label).
groove
Gives a slightly depressed look to the edge (as if there were a ditch around the text).
raised
The default; gives a 3D look with a shadow on the lower and right sides of the button, which causes it to look higher than the window surface.
ridge
The opposite of 'groove'; makes it look like a ridge is around the text.
sunken
The opposite of 'raised'; gives the 3D effect of being below the surface of the window.
No matter which value is specified for the -relief option, when the button is pressed with the mouse, its relief will change to 'sunken'.
0069-01.gif
Figure 3-9.
Different relief types for a button
In addition to changing the type of edge drawn around a button, you can also change the thickness of the edge by using -borderwidth:
-borderwidth => amount
The default -borderwidth is 2. The wider the -borderwidth, the more dramatic the effects of the -relief option become. Figure 3-10 shows what a borderwidth of 10 does to each relief type.
0069-02.gif
Figure 3-10.
Different relief types with -borderwidth set to 10
Borderwidth can also be specified by using -bd as an abbreviation. Although using -bd will obtain the same results, using -borderwidth makes your code eas-
ier to follow later on. Also, -bd isn't supported with all widgets, so relying on it can be dangerous.
I don't recommend using -borderwidth with values greater than 4, because it makes the widgets look extremely odd. In each of the widget chapters you'll find a screenshot showing what happens to the widget with a larger -borderwidth value for each of the possible -relief values. The best use of -borderwidth is making one widget stand out more than the others temporarily during development. (I also use it often with frames to figure out where the frame is. Normally they are invisible. See Chapter 12.)
Changing the Size of a Button
Normally, the size of the button is automatically determined by the application and is dependent on the text string or image displayed in the button. The width and height can be specified explicitly by using the -width and/or -height options:
-width => x, -height => y
The values specified for x and y change depending on whether a bitmap/image or text is displayed in the button. When a bitmap or image is displayed, the values in x and y represent valid screen distances. If text is displayed on the button, x and y are character sizes.
This example has one button that is default size and another that is drawn with -width of 10 and -height of 10. (It is not necessary that the amounts for -width and -height be the same or that you use both):
$mw->Button(-text => "Exit",
            -command => sub { exit })->pack(-side => 'left');
$mw->Button(-text => "Exit",
            -width => 10, -height => 10,
            -command => sub { exit })->pack(-side => 'left');
In Figure 3-11, the second button is much taller than it is wide because text characters are taller than they are wide.
0070-01.gif
Figure 3-11.
Example of button displaying default text and text with -width => 10, -height => 10
The value specified for both -width and -height are characters because the button is displaying text. When -width and -height are used with a bitmap, the
amount specified is in screen distance. Here is an example of using -width and -height with a bitmap:
$mw->Button(-bitmap => 'error',
            -width => 10, -height => 10,
            -command => sub { exit })->pack(-side => 'left');
$mw->Button(-bitmap => 'error',
            -command => sub { exit })->pack(-side => 'left');
$mw->Button(-bitmap => 'error',
            -width => 50, -height => 50,
            -command => sub { exit })->pack(-side => 'left');
The first button is created with a restriction on the width and height of 10. The middle button looks like it would normally. The third button is created with a width and height of 50. Figure 3-12 shows the resulting window.
0071-01.gif
Figure 3-12.
A bitmap displayed three times with different values for -width and -height
The default value for both -width and -height is 0. Using 0 allows the program to dynamically decide the height and width of the button.
The total width for buttons with text is calculated by the width the text takes up plus 2 × -padx amount. The height is the text height plus 2 × -pady amount. The width and height for buttons with a bitmap is just the width and height of the bitmap itself. Any -padx or -pady options are ignored when a bitmap is displayed.
As an alternative to specifying an explicit width or height, it is possible to increase the size of the button by using the options -padx and/or -pady to add padding between the text and edge of the button:
-padx => amount, -pady => amount
The amount specified with -pady is added to both the left and right side of the button. The amount specified with -pady is added to both the top and bottom of the button. Figure 3-13 shows an example.
By using these options you are telling the button to be sized larger than it normally would, but you don't have to worry that it will be sized too small, as you would if you explicitly set -width and -height.
Remember, -padx and -pady are ignored when a bitmap is displayed.
0072-01.gif
Figure 3-13.
Example of -padx => 20, -pady => 20
Adding a Keyboard Mapping
A button is traditionally invoked by clicking mouse button 1 when the mouse cursor is over the button. It can also be invoked by pressing the Tab key until the button has the keyboard focus and then pressing the spacebar. The effects are the same: The callback associated with the button is called, and the button -relief changes momentarily. The keyboard focus is visually indicated by a thin black rectangle drawn around the widget (see Figure 3-20 later in this chapter).
To allow an additional keyboard character to invoke the button, you can use the -underline option in a button displaying text:
-underline => N
This will underline the Nth character in the text string. The first character of the text string is the 0th character, so with the text string ''Exit", -underline => 1 will underline the second character in the string, the "x" (see Figure 3-14).
0072-02.gif
Figure 3-14.
Example of -underline => 1
The default value for -underline is -1, which means no characters will be underlined in the text string.
Color Options
The options that can change the button's colors are -background, -foreground, -activebackground, -activeforeground, and -disabledforeground. Each option takes a string that identifies a color. This string could either be a color description such as "blue" or a hex string such as "#d9d9d9", which also describes a color, but is much more cryptic.
For either Win32 or Unix systems you can run the widget demo included with the Tk module. If the perl/bin directory is in your path, you can simply type "widget"
on the DOS or Unix command line. Under the listbox section is an example that displays color names. You can double-click on the names in the list to see them change the application's color.
Valid values for the color string are available on your Unix system in a file called rgb.txt. Typically this file is located in the X11 lib directory. On my Linux system, it is located in /usr/X11R6/lib/rgb.txt. You can also use the X application xcolors or showrgb. Check the manpages for each command to determine the best way to use them.
Another place to look for valid color names (and this applies to Win32 as well) is in your Perl distribution directory. Look for the file xcolors.h. It is a text file that contains the RGB values and names for quite a few colors. I found this file in C:\Perl\lib\site\Tk\ptk on my Windows 95 machine.
The color of the button depends on the state the button is in at the time. When the button has a state of 'normal', the colors assigned to -foreground and -background are in effect. The background of the button is the area behind the text string but within the edges of the button.
The background is specified like this:
-background => color
The default background color is a light gray color ("#d9d9d9" in its hexadecimal RGB representation). Figure 3-15 shows the results of changing the second Exit button's background to blue.*
0073-01.gif
Figure 3-15.
Example of -background => 'blue'
The foreground of the button is the text (or bitmap) displayed. The foreground color is specified like this:
-foreground => color
By default -foreground is 'black'. Make sure that whatever color you pick contrasts enough with the background color to be readable. In the example in Figure 3-15, I left the text the default color, and it doesn't contrast very well with the background color of the button. If we change -foreground to 'white', then
* Although we are talking about color, the figures are in black and white. Unfortunately, using color figures would have made the book too expensive to produce. I've tried to make color choices that contrast so the figures look as good as possible. The best way to determine what happens with each color option is to experiment and run the examples.
we will be able to see the text much more easily, as you'll see in Figure 3-16. (The shortcut for -foreground is -fg, which may or may not work on other types of widgets. I suggest sticking with -foreground as the option name.)
0074-01.gif
Figure 3-16.
Example of -background => 'blue' and -foreground => 'white'
When you use the -foreground and -background options in conjunction with a bitmap, the bitmap foreground and background will change to the specified colors. The effect of the colors depends on the bitmap. See Figure 3-17.
0074-02.gif
Figure 3-17.
'error' bitmap with -foreground => 'white' and -background => 'black'
The -foreground and -background options control what color the button is when it is in the 'normal' state. When the button has the mouse cursor over it, the -activebackground and -activeforeground colors are used:
-activebackground => color, -activeforeground => color
These colors are different because we want users to have some visual clues that they can press the button. By having the colors change slightly when the mouse cursor is over the button, users know that the button can be pressed to do something. The default for -activebackground is a slightly darker gray color ("#ececec").
The final color option, -disabledforeground, is the color of the text when the button's state is 'disabled'.
-disabledforeground => color
When the button is in a disabled state, it will not respond when the mouse cursor is over it, or if it is pressed. The default for the color of the text (or bitmap) is "#a3a3a3". Figure 3-18 shows the difference between the text colors with one disabled button and one normal button. (We also saw this example in Figure 3-4. Lookthere for the code that created this window.)
0075-01.gif
Figure 3-18.
-disabledforeground example
Changing the Mouse Cursor
The mouse cursor normally looks like an arrow.* This can be changed on a widget-by-widget basis with the -cursor option:
-cursor => cursorname
When the mouse is over the button, the cursor will change to the one specified. The cursor change will happen whether the button is disabled or not. There is a large set of available cursors. Following is a list of cursors, and Figure 3-19 shows what they look like.
X_cursor
arrow
based_arrow_down
based_arrow_up
boat
bogosity
bottom_left_corner
bottom_right_corner
bottom_side
bottom_tee
box_spiral
center_ptr
circle
clock
coffee_mug
cross
cross_reverse
crosshair
diamond_cross
dot
dotbox
double_arrow
draft_large
draft_small
draped_box
exchange
fleur
gobbler
gumby
hand1
hand2
heart
icon
iron_cross
left_ptr
left_side
left_tee
leftbutton
II_angle
Ir_angle
man
middlebutton
mouse
pencil
pirate
plus
question_arrow
right_ptr
right_side
right_tee
rightbutton
rtl_logo
sailboat
sb_down_arrow
sb_h_double_arrow
sb_left_arrow
sb_right_arrow
sb_up_arrow
sb_v_double_arrow
shuttle
sizing
spider
spraycan
star
target
tcross
top_left_arrow
top_left_corner
top_right_corner
top_side
top_tee
trek
ul_angle
umbrella
ur_angle
watch
xterm
   

* What cursor is displayed is dependent on the window manager you are using, but most of the time it is an arrow.
0076-01.gif
Figure 3-19.
The standard cursors
Here's a program to look at the different cursors interactively:
#!/usr/bin/perl -w
use Tk;

## Create elements of window
$mw = MainWindow->new;
$mw->Button(-text => "Exit",
            -command => sub { exit })->pack(-side => "bottom",
                                      -fill => "x");
$scroll = $mw->Scrollbar;
$lb = $mw->Listbox(-selectmode => 'single',
                   -yscrollcommand => [set => $scroll]);
$scroll->configure(-command => [yview => $lb]);

$scroll->pack(-side => 'right', -fill => 'y');
$lb->pack(-side => 'left', -fill => 'both');

## Open file that contains all available cursors
## Might have to change this if your cursorfont.h is elsewhere
## On Win32 systems look in C:\Perl\lib\site\Tk\X11\cursorfont.h
open (FH, "/usr/X11R6/include/X11/cursorfont.h") ||
  die "Couldn't open cursor file.\n";
 
while (<FH>) {
  push (@cursors, $1) if (/\#define XC_(\w+) /);
}

close(FH);

$lb->insert('end', sort @cursors);
$lb->bind('<Button-1>',
     sub { $mw->configure(-cursor => $lb->get($lb->curselection)); });

MainLoop;
Although this program might seem a bit complicated at this point in the book, take a look at how it does things. If you don't completely understand it right away, it's okay. Keep reading for a few chapters and then come back and look at it again until it starts to sink in. For reference, listboxes are covered in Chapter 7, The Listbox Widget, and bind is covered in Chapter 14, Binding Events.
Focus Options
In an application, you can tab between widgets to make them available for input from the keyboard. The application indicates that a widget is available for keyboard input by drawing an outline around it in black (this is called the highlight rectangle; see Figure 3-20). If a widget has this outline around it, it is said to have the focus of the application. (You can force the focus of an application to start with a specific widget by using $widget->focus;.) Once a button has the focus, you can use the spacebar on your keyboard to activate it instead of using the mouse.
0077-01.gif
Figure 3-20.
The first button has the input focus.
You can force the application to not allow your button to receive the keyboard focus at all by using the -takefocus option:
-takefocus => 0 | 1 | undef
The -takefocus option is normally set to an empty string (undef), which allows the application to dynamically decide if the widget will accept focus. If a widget has its state set to 'disabled', it will be skipped over when users tab through all the widgets. To have the application always ignore the widget when tabbing through, use -takefocus => 0. To have the application always allow focus to the widget, use -takefocus => 1.
Altering the Highlight Rectangle
The highlight rectangle* is normally displayed with a thickness of 2 pixels. This can be changed by using the -highlightthickness option:
-highlightthickness => amount
The amount specified is any valid screen distance. In Figure 3-21, the Exit button on the right has a -highlightthickness of 10 and has the focus.
0078-01.gif
Figure 3-21.
Example of -highlightthickness => 10
When the button doesn't have the keyboard focus, a small space is left around it. If this extra space bothers you, you can set -highlightthickness to 0 and the space won't display even if that widget has the focus. It is bad style to set the -highlightthickness to 0 if you aren't also setting -takefocus to 0.
The color of the highlight rectangle can also be changed. There are two values for this: the color of the highlight rectangle when the button does not have the focus and the color of the highlight rectangle when it does have the focus. The option -highlightcolor is the color of the highlight rectangle when the button does have focus:
-highlightcolor => color
Figure 3-22 shows the right button with the focus and with -highlightcolor set to 'yellow'. Compare it to the picture in Figure 3-21 to see the difference.
0078-02.gif
Figure 3-22.
Example of button with -highlightcolor => 'yellow'
To change the color of the space left around the button when it doesn't have the focus, use the option -highlightbackground:
-highlightbackground => color
* On Win32 systems, the highlight rectangle is drawn as a dashed line within the widget.
Normally, the highlight rectangle is the same color as the background of the window, which allows it to blend in with the background of the window or frame that contains the button.
Figure 3-23 shows an example where both buttons have the following configuration:
-highlightcolor => 'blue', -highlightbackground => 'yellow'
The right button is the one that has the focus.
0079-01.gif
Figure 3-23.
Example of button with -highlightcolor => 'blue' and -highlightbackground => 'yellow'
Configuring a Button
After creating the widget and saving a reference to it in a scalar (such as $button), it is possible to use methods on that button.
There are two methods available to configure a button after it is created and to get configuration information back: configure and cget. They are generic to all widgets and are covered in Appendix A, Configuring Widgets with configure and cget. Here are some common examples to get you started:
$state = $button->cget(-state);
# Get the current value for -state
$state = $button=>configure(-state);
#Get the current value for -state
$button->configure(-text => "New Text");
Change the text
$text = $button->cget(-text);
# Get the current text value
@all = $button->configure();
# Get info on all options for button

Flashing the Button
The flash method will cause the button to appear to be "flashing" on the screen. It changes back and forth from the normal state colors to the active state colors:
$button->flash();
Invoking the Button
The invoke method invokes the subroutine to which the -command option points. Once you use -command to assign the callback, then anytime you need to perform that same task, you can use invoke():
$button->invoke();
Some Fun Things to Try
One of the best ways to figure out how Perl/Tk works is to try it. Once you understand the basics, you'll spend most of your time tweaking options and callbacks to do the correct thing. Here are some ways to learn about the button widget:
• Create a window with three buttons in it. Have each button print something different when clicked on.
• Create a window with three buttons. Have the first two buttons change each other's text when pressed. The last button should allow you to exit the program.
• Make some really big buttons and some really tiny buttons all in the same window.