Events, bindings and callbacks
6.5 Binding events and callbacks
The examples so far have demonstrated how to bind an event handler to an instance of a wid-get so that its behavior on receiving an event will not be inherited by other instances of the widget. Tkinter provides the flexibility to bind at several levels:
1 At the application level, so that the same binding is available in all windows and widgets in the application, so long as one window in the application has focus.
2 At the class level, so that all instances of widgets have the same behavior, at least initially.
3 At the shell (Toplevel or root) level.
4 At the instance level, as noted already.
Binding events at the application and class level must be done carefully, since it is quite easy to create unexpected behavior in your application. In particular, indiscriminate binding at the class level may solve an immediate problem, but cause new problems when new func-tionality is added to the application.
It is generally good practice to avoid creating highly nonstandard behavior in widgets or interfaces with which the user is familiar. For example, it is easy to create bindings which allow an entry field to fill in reverse (so typing 123 is displayed as 321), but this is not typical entry behavior and it might be confusing to the user.
6.5.1 Bind methods
You will find more information on bind and unbind methods in “Common options” on page 425, so in this section, I will just illustrate bind methods in the context of the four bind-ing levels.
Application level
Applications frequently use F1 to deliver help. Binding this keysym at the application level means that pressing F1, when any of the application’s windows have focus, will bring up a help screen.
Note
B I N DIN G E V E NT S A ND C A L L B A C K S
105
Class level
Binding at the class level allows you to make sure that classes behave uniformly across an application. In fact, Tkinter binds this way to provide standard bindings for widgets. You will probably use class binding if you implement new widgets, or you might use class binding to provide audio feedback for entry fields across an application, for example.
Toplevel window level
Binding a function at the root level allows an event to be generated if focus is in any part of a shell. This might be used to bind a print screen function, for example.
Instance level
We have already seen several examples of this, so we will not say any more at this stage.
The following hypothetical example illustrates all four of the binding modes together.
from Tkinter import * def displayHelp(event):
frame = Frame(root, takefocus=1, highlightthickness=2)
text = Entry(frame, width=10, takefocus=1, highlightthickness=2) root.bind_all('<F1>', displayHelp)
text.bind_class('Entry', '<KeyPress>', lambda e, x=101: sayKey(e,x)) root.bind('<Alt_L>', printWindow)
frame.bind('<Control-Shift-Down>' , cursor) text.bind('<Control-Shift-Up>', unbindThem)
Example_6_3.py
1
0 unbind all bindings 2
3 4
6 5
106
C H A P T E R 6 E V E N T S , B I N D I NG S A N D C A L L B A C K SFirst, the callbacks are defined. These are all simple examples and all but the last one take account of the event object being passed as the callback’s argument, from which we extract the keysym of the key generating the event.
def displayHelp(event):
print 'hlp', event.keysym
Although the class-level binding was made with a method call to an Entry widget, bind_class is an inherited method, so any instance will work and root.unbind_class is quite acceptable. This is not true for an instance binding, which is local to the instance.
We make an application-level binding:
root.bind_all('<F1>', displayHelp)
In this class-level binding we use a lambda function to construct an argument list for the callback:
text.bind_class('Entry', '<KeyPress>', lambda e, x=101: sayKey(e,x))
Here we make a toplevel binding for a print-screen callback:
root.bind('<Alt_L>', printWindow)
Finally, we make instance bindings with double modifiers:
frame.bind('<Control-Shift-Down>', cursor) text.bind('<Control-Shift-Up>', unbindThem)
Be prepared to handle multiple callbacks for events if you use combinations of the four binding levels that have overlapping bindings.
Tkinter selects the best binding at each level, starting with any instance bind-ings, then toplevel bindbind-ings, followed by any class bindings. Finally, application level bindings are selected. This allows you to override bindings at any level.
6.5.2 Handling multiple bindings
As I mentioned in the note above, you can bind events at each of the four binding events.
However, because events are propagated, that might not result in the behavior that you intended.
For a simple example, suppose you want to override the behavior of a widget, and rather than have BACKSPACE remove the previous character, you want to insert \h into the widget. So you set up the binding like this:
text.bind('<BackSpace>', lambda e: dobackspace(e))
and define the callback like this:
def dobackspace(event):
event.widget.insert(END, '\\h')
1
DY N A M I C C A L L B A C K H A N DL E R S
107
Unfortunately this doesn’t work, because the event is bound at the application level. The widget still has a binding for BACKSPACE, so after the application level has been invoked and
\h has been inserted into the widget, the event is propagated to the class level and the h is removed.
There is a simple solution: return “break” from the last event handler that you want to propagate events from and the superior levels don’t get the event. So, the callback looks like this:
def dobackspace(event):
event.widget.insert(END, '\\h') return "break"