When creating a Button instance, the -command option specifies the callback to invoke when the user presses the Button. The button press must be button 1, because that's the Button's documented behavior. As a convenience, the Button constructor automatically creates the link between the button 1 press and our callback using the bind command. If it didn't, we'd have to do it manually for every Button we create, using syntax similar to this:
$button->bind('<ButtonRelease-1>' => callback);
If nothing else, -command => callback is fewer characters to type, but it also provides consistency, because the Button always reacts to the first button, not whatever button the programmer decided to use.
In the previous bind command, the string <ButtonRelease-1> is know as an event descriptor. It's composed of two fields enclosed in angle brackets, the event type and the event detail. In the case of a ButtonRelease event type, the detail portion specifies which button we are interested in. The event descriptor in this example is very specific: it invokes the callback only when button 1 is released over the Button widget (as opposed to when it's pressed). If you watch a Button closely, pressing button 1 only changes the widget's relief from raised to sunken. If you move the cursor away from the Button, the relief changes back, but the widget's callback is never invoked.
An event descriptor can be more complex than our first example; it can actually be one or more event patterns, and each pattern can have zero or more modifiers:
<modifier-modifier-type-detail>
In the previous example, the event descriptor was comprised of one event pattern, which is typically all you'll ever use. Any of the fields may be omitted, as long as at least type or detail is present.
Tk also supports user defined virtual events. They are named entities surrounded by double angle brackets:
<<virtual-event-name>>
Virtual events may not have modifiers. In previous chapters, we've discussed these virtual events: Tk::Text <<Undo>> and <<Redo>>, Tk::Menu <<MenuSelect>>, and Tk::Listbox <<ListboxSelect>>.
Use the eventGenerate command described later to trigger a virtual event.
Table 15-2 lists the valid modifiers. Double and Triple modifiers repeat events. They are most often associated with buttons, so we often see event descriptors like <Double-Button-1>. Common keyboard modifiers include Alt, Control, Meta, Mod, and Shift; thus, <Control-Key-c> would trap a Control-c.
Alt |
Control |
Mod3, M3 |
Button1, B1 |
Double |
Mod4, M4 |
Button2, B2 |
Lock |
Mod5, M5 |
Button3, B3 |
Meta, M |
Shift |
Button4, B4 |
Mod1, M1 |
Triple |
Button5, B5 |
Mod2, M2 |
An event descriptor can include any of the types described in Table 15-3.
Event type |
Brief description |
---|---|
Activate |
Currently unused. |
ButtonPress (or Button) |
A mouse button was pressed. |
ButtonRelease |
A mouse button was released. |
Circulate |
A widget's stacking order has changed. |
ColorMap |
A widget's colormap has changed. |
Configure |
A widget has changed size or position and may need to adjust its layout. |
Deactivate |
Currently unused. |
Destroy |
A widget was destroyed. |
Enter |
The cursor has moved into a widget. |
Expose |
All or part of a widget has been uncovered and may need to be redrawn. |
FocusIn |
A widget has gained the keyboard focus. |
FocusOut |
A widget has lost the keyboard focus. |
Gravity |
A widget has moved because its parent changed size. |
KeyPress (or Key) |
A key has been pressed. |
KeyRelease |
A key has been released. |
Motion |
The cursor is in motion over a widget. |
MouseWheel |
The mousewheel is scrolling. |
Leave |
The cursor has moved out of a widget. |
Map |
A widget has been mapped onto the display and is visible. |
Property |
A widget property has changed. |
Reparent |
A widget has been reparented. |
Unmap |
A widget has been unmapped from the display and is no longer visible. |
Visibility |
A widget's visibility has changed. |
Of all these event types, most of the time you'll only deal with ButtonPress, ButtonRelease, Destroy, Enter, KeyPress, KeyRelease, Leave, and Motion.
We know that for Button events, the detail field of the event descriptor is a button number. Valid numbers are one through five. If the Button detail is omitted, any button triggers the callback. For Key events (KeyPress and KeyRelease), the detail field is a keysym, an identifier for the desired keyboard character. For alphabetic characters, the keysym is simply the character itself. For example:
$mw->bind('<KeyRelease-a>' => callback);
invokes the callback when the lowercase character "a" is typed in the MainWindow. If you want to bind to an uppercase character, use the uppercase keysym:
$mw->bind('<KeyRelease-A>' => callback);
Other keysyms are not so easy to figure out; for instance, what's the keysym for the page-down key? Well, let's find out....
When Tk invokes a callback, it provides detailed information about the event that triggered the callback. In C, this data is stored in a structure and has been historically called the event structure. The internal Tk event structure is still a real C structure, but we don't fiddle with it directly. Instead, Perl/Tk gives us an event object, which we use to call methods that return the pieces of data of interest to us.
To see how this works, let's examine a program that prints the keysym for any keyboard character:
$mw->bind('<KeyPress>' => \&print_keysym); sub print_keysym { my($widget) = @_; my $e = $widget->XEvent; # get event object my($keysym_text, $keysym_decimal) = ($e->K, $e->N); print "keysym=$keysym_text, numeric=$keysym_decimal\n"; }
Notice the KeyPress binding is for the MainWindow, which lets us type anywhere in the window, even if it's filled with other widgets. The KeyPress event descriptor is missing its detail field, which means the callback is invoked when any key is pressed. Also notice that we've used a callback syntax that doesn't allow us to pass explicit arguments to print_keysym.
But print_keysym is expecting an argument; in fact, Tk implicitly passes the bound widget reference as the first argument to the callback, adding any of our explicit arguments afterwards. This is usually what we want, but sometimes the implicit argument gets in our way. To prevent bind from supplying the widget reference, specify your own object:
$a->bind(event_desciptor => [$b => callback]);
bind invokes the callback with widget $b rather than $a.
Using the widget reference, we call XEvent, which returns the event object for the KeyPress. The K method returns the key symbol, and the N method returns its decimal value.
In case you're wondering, the keysym for page down is Next.
The two most important pieces of information a callback needs are the event object and the widget the event object applies to. In newer Tks, Nick introduced two localized variables that represent this information: $Tk::event and $Tk::widget. These fully qualified variables are available to any callback. If you're particularly lazy, import them like so:
use Tk ':variables';
Then you can use the unqualified names $event and $widget in your callbacks. With this new information, we can write our keysym program more succinctly:
$mw->bind('<KeyPress>' => sub { print 'Keysym=', $Tk::event->K, ', numeric=', $Tk::event->N, "\n"; });
In the following example, we see the three different ways to get the event's widget reference:
my $b = $mw->Button(-text => 'Click B1 Then B2', -command => \&callback); $b->bind('<ButtonRelease-2>' => \&callback); sub callback { print "\n"; print "callback args = @_\n"; print "\$Tk::event = $Tk::event\n"; print "\$Tk::widget = $Tk::widget\n"; print "\$Tk::event->W = ", $Tk::event->W, "\n"; }
Clicking button 1 invokes callback with no arguments, and we see that $Tk::widget and the W event information method both return the same widget reference (that of the Button). Clicking button 2 invokes callback again, but this time, Tk supplies the bind widget reference as an argument: the Button reference.
callback args = $Tk::event = XEvent=SCALAR(0x82920f0) $Tk::widget = Tk::Button=HASH(0x817fa00) $Tk::event->W = Tk::Button=HASH(0x817fa00) callback args = Tk::Button=HASH(0x817fa00) $Tk::event = XEvent=SCALAR(0x817ff70) $Tk::widget = Tk::Button=HASH(0x817fa00) $Tk::event->W = Tk::Button=HASH(0x817fa00)
Table 15-4 lists all the event information methods. Keep in mind that not all information is applicable to all events. For conciseness, we also list the corresponding eventGenerate options. The Tk::event documentation has more complete information.
Method/option |
Valid events |
Comments |
---|---|---|
#[39] /
-serial
|
All events |
Integer |
@ |
Events with x/y fields |
"@x,y" used by Tk::Text |
A |
KeyPress, KeyRelease |
ASCII character |
a / -above |
Configure |
Window object or ID |
B / -borderwidth |
Configure |
Screen distance |
b / -button |
ButtonPress, ButtonRelease |
Button number |
c / -count |
Expose, Map |
Integer |
D / -delta |
MouseWheel |
Integer |
d / -detail |
Enter, Leave, FocusIn, FocusOut |
See Tk::event POD |
E / -sendevent |
All events |
Boolean |
f / -focus |
Enter, Leave |
All events |
h / -height |
Configure |
Screen distance |
K / -keysym |
KeyPress, KeyRelease |
Symbolic keysym |
k / -keycode |
KeyPress, KeyRelease |
Integer |
m / -mode |
Enter, Leave, FocusIn, FocusOut |
See Tk::events POD |
N |
KeyPress, KeyRelease |
Decimal keysym |
o / -override |
Map, Reparent, Configure |
Boolean (overrideredirect) |
p / -place |
Circulate |
See Tk::event POD |
R / -root |
KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion |
Window object or ID |
S / -subwindow |
KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion |
Window object or ID |
s / -state |
All events |
See Tk::event POD |
T |
All events |
The event type |
t / -time |
KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion, Property |
Integer |
W |
All events |
Widget reference |
/ -when |
All events |
now | tail | head | markSee Tk::event POD |
w / -width |
Configure |
Screen distance |
X / -rootx |
KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion |
Screen distance (the event's x coordinate relative to the root window) |
x / -x |
KeyPress, KeyRelease, ButtonPress, ButtonRelease, Motion, Enter, Leave, Expose, Configure, Gravity, Reparent |
Screen distance (the event's x coordinate relative to the widget) |
Y/ -rooty |
KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion |
Screen distance (the event's y coordinate relative to the root window) |
y / -y |
KeyPress, KeyRelease, ButtonPress, ButtonRelease, Motion, Enter, Leave, Expose, Configure, Gravity, Reparent |
Screen distance (the event's y coordinate relative to the widget) |
Like most widgets, Buttons have a default behavior defined by bindings automatically created by Perl/Tk. That's why when we make a Button, we don't have to create its <ButtonRelease-1> binding. These default widget bindings are known as class bindings. We can see these bindings by using a second form of the bind command, where we pass it just a class name. bind then reports all the event descriptors for that class. We use the Perl built-in function ref to determine the widget's class:
my $b = $mw->Button(qw/-text Beep -command/ => sub {$mw->bell}); $b->pack; my $class = ref $b; print "Button \$b is an instance of class '$class'.\n" . "This class has bindings for these events:\n\n"; print join("\n", $b->bind($class) ), "\n";
This produces:
Button $b is an instance of class 'Tk::Button'. This class has bindings for these events: <Key-Return> <Key-space> <ButtonRelease-1> <ButtonPress-1> <Leave> <Enter>
Without even referring to the Tk::Button documentation, we can guess what most of these bindings do. The <Enter> event is triggered when the cursor moves over the Button, and the Button's background color changes, indicating it's activated. The <Leave> event restores the Button's background color. The <ButtonPress-1> event changes the Button's relief to sunken, and the <ButtonRelease-1> event changes the relief back to raised and invokes the -command callback. The Key events also invoke the callback if the Button has the input focus.
You can add additional widget bindings to the class if you desire, so that all Buttons inherit this new behavior. Suppose you want button 2 to execute a Button callback twice. Here's how to do it:
my $b = $mw->Button(qw/-text Beep -command/ => sub {$mw->bell}); $b->pack; my $class = ref $b; $b->bind($class, '<ButtonRelease-2>' => \&twice); print "Button \$b is an instance of class '$class'.\n" . "This class has bindings for these events:\n\n"; print join("\n", $b->bind($class) ), "\n"; sub twice { my $button = shift; $button->Callback(-command); $button->Callback(-command); }
This produces:
Button $b is an instance of class 'Tk::Button'. This class has bindings for these events: <ButtonRelease-2> <Key-Return> <Key-space> <ButtonRelease-1> <Button-1> <Leave> <Enter>
Here we used a third variant of bind that ties an event to a class as a whole. There are three important facts to note:
We've used a named subroutine rather than an anonymous subroutine for the callback. While not strictly required, it's still good style, because it lets others override the callback by providing their own subroutine of the same name. This is particularly relevant when writing mega-widgets.
The new binding is retroactive. Widget instances of the class created prior to the new binding definition automatically inherit the new binding.
The Callback method is the proper way to invoke a Perl/Tk callback. It works like this: Callback takes its object (here, the Button widget), looks up the value of the option passed as its argument (here, -command), then invokes the callback. Callback takes care of the argument handling on our behalf; all the information it needs is contained in the Tk::Callback object.
Sometimes you want a binding placed on a particular widget instance instead of the entire class. If you want one particular Button to invoke its callback twice, use this familiar bind format:
$b->bind('<ButtonRelease-2>' => \&twice);
To query instance bindings, use this fourth flavor of the bind command:
print $b->bind, "\n";
Which yields:
<ButtonRelease-2>
This is as expected. Remember, all other Button bindings are class bindings.
Table 15-5 shows bind syntax. tag represents a Tk class name, a widget reference, or a symbolic bindtags tag. We examine bindtags in the next section.
Comments
$w->bind;
Query $w for its event descriptors (same as $w->bind($w);).
$w->bind(tag);
Query tag for its event descriptors.
$w->bind(event_descriptor);
Query $w's event_descriptor for its callback.
$w->bind(tag, event_descriptor);
Query tag's event_descriptorfor its callback.
$w->bind(event_descriptor => callback);
Set callback for $w.
$w->bind(tag, event_descriptor => callback);
Set callback for tag.
There are two callback formats we haven't yet talked about. They both query for the actual callback associated with an event descriptor, and you might wonder how they can be useful in the Perl/Tk world, where callbacks are code references. Well, the callbacks may be method names as well, and if we query for a callback, we might get a method name (as a string) instead of a code reference. One thing we can do with this information is write a drop-in replacement for the named subroutine in a widget subclass. Tk will invoke our new subroutine in deference to the superclass method. We can simulate this in non-mega-widget code using the _ _PACKAGE_ _ construct. Here's a way of rewriting the previous instance binding as a fake method name:
$b->bind('<ButtonRelease-2>' => __PACKAGE__ . '::twice');
Now Tk invokes the named subroutine in the named package (usually package main). You do not want to qualify the subroutine with an explicit package name in a mega-widget, though; Perl will find the method via its normal lookup mechanism.
Here is example code for a hypothetical calculator that binds the digits and arithmetic operators that drive the calculator, including those on the numeric keypad:
foreach my $key ( qw/0 1 2 3 4 5 6 7 8 9/ ) { $mw->bind( "<Key-$key>" => [\&key, $key] ); $mw->bind( "<KP_$key>" => [\&key, $key] ); } foreach my $key ( qw/period KP_Decimal/ ) { $mw->bind( "<$key>" => [\&key, '.'] ); } foreach my $key ( qw/Return KP_Enter/ ) { $mw->bind( "<$key>" => \&enter ); } foreach my $key ( qw/plus KP_Add/ ) { $mw->bind( "<$key>" => [\&math3, $ad, $io, undef] ); } foreach my $key ( qw/minus KP_Subtract/ ) { $mw->bind( "<$key>" => [\&math3, $sb, undef, undef] ); } foreach my $key ( qw/asterisk KP_Multiply/ ) { $mw->bind( "<$key>" => [\&math3, $ml, $an, $dm] ); } foreach my $key ( qw/slash KP_Divide/ ) { $mw->bind( "<$key>" => [\&math3, $dv, $xr, $dd] ); } $mw->bind( '<Delete>' => \&bspclrx );
Many machines of an Intel architecture include an IntelliMouse, a mouse with a wheel sandwiched between its two buttons. In a Unix environment, Linux in particular, the wheel acts as the middle button. Thus, one has full three-button capabilities. In a Win32 environment, however, the wheel serves as a scrolling device. As it happens, Tk can also use the wheel to scroll.
The following code is taken from Slaven Rezic's post on comp.lang.perl.tk. At last, we Unix Perl/Tk-ers can use the MouseWheel event. Slaven tested the code under NT, and we have tested it under Linux.
Until BindMouseWheel becomes part of core Perl/Tk, you can use code similar to this:
#!/usr/local/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; my $t = $mw->Text->pack; $t->insert('end', "line $_\n") for (1 .. 200); $t->focus; &BindMouseWheel($t); MainLoop; sub BindMouseWheel { my($w) = @_; if ($^O eq 'MSWin32') { $w->bind('<MouseWheel>' => [ sub { $_[0]->yview('scroll', -($_[1] / 120) * 3, 'units') }, Ev('D') ] ); } else { # Support for mousewheels on Linux commonly comes through # mapping the wheel to buttons 4 and 5. If you have a # mousewheel ensure that the mouse protocol is set to # "IMPS/2" in your /etc/X11/XF86Config (or XF86Config-4) # file: # # Section "InputDevice" # Identifier "Mouse0" # Driver "mouse" # Option "Device" "/dev/mouse" # Option "Protocol" "IMPS/2" # Option "Emulate3Buttons" "off" # Option "ZAxisMapping" "4 5" # EndSection $w->bind('<4>' => sub { $_[0]->yview('scroll', -3, 'units') unless $Tk::strictMotif; }); $w->bind('<5>' => sub { $_[0]->yview('scroll', +3, 'units') unless $Tk::strictMotif; }); } } # end BindMouseWheel
There's an interesting item here. Notice the funny Ev('D') construct in the Win32 callback. This is the Perl/Tk way of postponing argument evaluation until the callback is executed. Here, it's the D field (MouseWheel delta) from the event structure. Equivalently, we could omit the Ev call and use the Tk::event object to manually fetch the mousewheel delta within the callback:
my $delta = $Tk::event->D;
where $delta corresponds to $_[1] in the callback.
Ev is even more sophisticated. You can pass it yet another Perl/Tk callback that doesn't get evaluated until the main event callback is executed. And Ev is recursive, so an Ev call can contain other Ev calls.
Some final notes. A Canvas widget has its own bind method that binds callbacks to individual Canvas items rather than the Canvas as a whole. Unsurprisingly, the syntax parallels the normal bind:
$canvas->bind(tagorid, event_descriptor => callback);
where tagorid identifies the particular Canvas item. To create a binding for the Canvas instance, we use this special method:
$canvas->CanvasBind(event_descriptor => callback);
If CanvasBind isn't available with your version of Perl/Tk, you can always fall back to the old syntax:
$canvas->Tk::bind(event_descriptor => callback);
Copyright © 2002 O'Reilly & Associates. All rights reserved.