Version 12 of Bindings and why they are important

Updated 2011-10-19 10:26:18 by dkf

Purpose: explain the Tk concept of bindings. List the different kinds of bindings available. Show a practical example of use.


Bindings are the connections between widgets that create events, and the code (procedures--except that, properly speaking, Tcl bindings have always been to scripts) that handle the events.

 proc makeButton { buttonName buttonText } {
    button .$buttonName -text $buttonText
    bind .$buttonName <Button-1> "$buttonText"
    bind .$buttonName <Button-2> "destroy .$buttonName"
    bind .$buttonName <Button-3> "puts \"$buttonText\""
    pack .$buttonName 
 }

Bindings are a specialisation of the general concept of event handlers to Tk's external event mechanism, and they are needed because GUIs are asynchronous things; you don't know what a user is going to do next, and you have to handle a lot of different possibilities. The only alternative to the system of having events is to use a finite state machine, but writing one of those for a modern GUI is a terrifying concept!

Most system programming languages use callbacks to handle events, but in Tcl it is far more natural to directly nominate a piece of code to execute whenever a particular event arrives, especially as this can always call a procedure if needed. It is this code that is called a binding (because of the command used to manage this association; bind.)

But now for an example:

  pack [label .l -textvariable msg]
  set msg "Dum de dum de dum..."
  bind .l <Enter> {set msg "Hello friend"}
  bind .l <ButtonPress-1> {set msg "You're touching me!"}
  bind .l <ButtonRelease-1> {set msg "You've let go."}
  bind .l <Leave> {set msg "Dum de dum de dum..."}
  bind .l <3> {bell}

Ken Jones explains how to think about default bindings in a Usenet posting [L1 ].

A quick examination of the default Button bindings indicates that a button needs to receive an <Enter>, <ButtonPress-1>, <ButtonRelease-1> sequence to invoke its associated command. It looks as though it's also a good idea to generate a <Leave> to clean things up afterwards (as that's the sequence of events a Button would normally receive if a user were directly interacting with the application).

In case you weren't aware of how to do so, you can examine the default widget bindings in a couple of ways. One is by using the Tcl bind command to introspect the bindings. Just give bind the widget class name as an argument, and it returns a list of all events that have bindings for that widget class. For example:

bind Button

To examine a binding action, execute bind with both the target (e.g., the widget class) and the event specification. For example:

bind Button <Enter>

You'll notice that the default widget bindings call a variety of pre-defined procedures. For example, in Tcl 8.3 the action for receiving an <Enter> event on a Button is to execute tkButtonEnter, passing the name of the button widget that received the event as an argument. You can use the info args and info body commands to then introspect on these pre-defined procedures.

Of course, an even easier way to examine all of the built-in bindings is to browse through the library code that implements them all. These are simply Tcl scripts that are automatically sourced when a Tk application starts. To find them, go to the directory where Tcl is installed on your system, then go to the lib/tkX.X subdirectory (where X.X is the version of Tcl/Tk you've got installed). The file tk.tcl gets sourced automatically, and it in turn sources most of the other files in that directory. Of note in this situation is the button.tcl file, which implements the Button, Checkbutton, and Radiobutton bindings and their actions.