home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Book HomeMastering Perl/TkSearch this book

15.2. Binding to Events

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.

15.2.1. Event Descriptor Syntax

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.

15.2.1.1. Event descriptor modifiers

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.

Table 15-2. Event modifiers

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

15.2.1.2. Event descriptor types

An event descriptor can include any of the types described in Table 15-3.

Table 15-3. Legal event types

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....

15.2.2. The Event Structure

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.

15.2.2.1. The exporter tag :variables

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)

15.2.2.2. Event information methods

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.

Table 15-4. Event information methods

Method/option

Valid events

Comments

#[39] / -serial
Since # is an illegal method name, you must store it in a variable: $sn = '#' ; $Tk::event->$sn().

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)

15.2.3. Widget Class Bindings

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:

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 );

15.2.5. Binding to a MouseWheel Event

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.

15.2.6. Canvas Bindings

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);


Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.