Event Handling

So far we have covered the widgets types, how instances of them are created, how their attributes can be set and queried, and how they can be managed for display using geometry managers. What we have not touched on is how to give each widget a behavior.

This is done through event handlers. Each widget instance can be given a window event handler for each kind of window event. A window event is something like the cursor moving into or out of the widget, a key press happening while the widget is active (in focus), or the widget being destroyed.

Event handlers are specified through the bind command:

     bind widgetName eventSequence command
     

where widgetName is the name or class of the widget to which the event handler should be attached, eventSqueuence is a description of the event that this event handler will handle, and command is a script that is invoked when the event happens (i.e. it is the event handler).

Common event types are

Key
KeyPress
when a key was pressed
KeyRelease
when a key was released
Button
ButtonPress
when a mouse button was pressed
ButtonRelease
when a mouse button was released
Enter
when the cursor moves into a widget
Leave
when the cursor moved our of a widget
Motion
when the cursor moves within a widget

There are other event types. Please refer to the Tk documentation for a complete list.

The eventSequence part of a bind command is a list of one or more of these events, each event surrounded by angled brackets. (Mostly, an event sequence consists of handling a single event. Later we will show more complicated event sequences.)

An example is the following:

     button .b -text "click me"
     pack .b
     bind .b <Enter> { puts "entering .b" }
     

makes a button .b displaying text click me and displays it in the root window using the packing geometry manager. The bind command specifies that when the cursor enters (i.e. goes onto) the widget, then the text entering .b is printed at the terminal.

We can make the button change color as the cursor enters or leaves it like this:

     button .b -text "click me" -background red
     pack .b
     bind .b <Enter> { .b config -background blue }
     bind .b <Leave> { .b config -background red }
     

which causes the background color of the button to change to blue when the cursor enters it and to change back to red when the cursor leaves.

An action can be appended to an event handler by prefixing the action with a + sign. An example is:

     bind .b <Enter> {+puts "entering .b"}
     

which, when added to the example above, would not only change the color of the button to red when the cursor enters it, but would also print entering .b to the terminal.

A binding can be revoked simply by binding the empty command to it:

     bind .b <Enter> {}
     

A list of events that are bound can be found by querying the widget thus:

     bind .b
     

which will return a list of bound events.

To get the current command(s) bound to an event on a widget, invoke bind with the widget name and the event. An example is:

     bind .b <Enter>
     

which will return a list of the commands bound to the event <Enter> on widget .b.

Binding can be generalized to sequences of events. For example, we can create an entry widget that prints spells rob each time the key sequence ESC r o b happens:

     entry .e
     pack .e
     bind .e <Escape>rob {puts "spells rob"}
     

(A letter on its own in an event sequence stands for that key being pressed when the corresponding widget is in focus.)

Events can also be bound for entire classes of widgets. For example, if we wanted to perform the same trick for ALL entry widgets we could use the following command:

     bind entry <Escape>rob {puts "spells rob"}
     

In fact, we can bind events over all widgets using all as the widget class specifier.

The event script can have substitutions specified in it. Certain textual substitutions are then made at the time the event is processed. For example, %x in a script gets the x coordinate of the mouse substituted for it. Similarly, %y becomes the y coordinate, %W the dot path of the window on which the event happened, %K the keysym of the button that was pressed, and so on. For a complete list, see the manual.

In this way it is possible to execute the event script in the context of the event.

A clever example of using the all widget specifier and text substitutions is given in John Ousterhout's book on Tcl/Tk (see Resources):

     bind all <Enter> {puts "Entering %W at (%x, %y)"}
     bind all <Leave> {puts "Leaving %W at (%x, %y)"}
     bind all <Motion> {puts "Pointer at (%x, %y)"}
     

which implements a mouse tracker for all the widgets in a Tcl/Tk application. The widget's name and x and y coordinates are printed at the terminal when the mouse enters or leaves any widget, and also the x and y coordinates are printed when the mouse moves within a widget.