You are here

Binding

8 September, 2015 - 10:43

A binding is an association between a widget, an event and a callback: when an event (like a button press) happens on a widget, the callback is invoked.

Many widgets have default bindings. For example, when you press a button, the default binding changes the relief of the button to make it look depressed. When you release the button, the binding restores the appearance of the button and invokes the callback specified with the command option.

You can use the bind method to override these default bindings or to add new ones. For example, this code creates a binding for a canvas (you can download the code in this section from http://thinkpython.com/code/draggable_demo.py):

ca.bind('<ButtonPress-1>', make_circle)

The first argument is an event string; this event is triggered when the user presses the left mouse button. Other mouse events include ButtonMotion, ButtonRelease and Double-Button.

The second argument is an event handler. An event handler is a function or bound method, like a callback, but an important difference is that an event handler takes an Event object as a parameter. Here is an example:

def make_circle(event):pos = ca.canvas_coords([event.x, event.y])item = ca.circle(pos, 5, fill='red')

The Event object contains information about the type of event and details like the coordinates of the mouse pointer. In this example the information we need is the location of the mouse click. These values are in “pixel coordinates,” which are defined by the underlying graphical system. The method canvas_coords translates them to “Canvas coordinates,” which are compatible with Canvas methods like circle.

For Entry widgets, it is common to bind the <Return> event, which is triggered when the user presses the Return or Enter key. For example, the following code creates a Button and an Entry.

bu = g.bu('Make text item:', make_text)en = g.en()en.bind('<Return>', make_text)

make_text is called when the Button is pressed or when the user hits Return while typing in the Entry. To make this work, we need a function that can be called as a command (with no arguments) or as an event handler (with an Event as an argument):

def make_text(event=None):text = en.get()item = ca.text([0,0], text)

make_text gets the contents of the Entry and displays it as a Text item in the Canvas.

It is also possible to create bindings for Canvas items. The following is a class definition for Draggable, which is a child class of Item that provides bindings that implement dragand-drop capability.

class Draggable(Item):
 
def __init__(self, item):
self.canvas = item.canvas
self.tag = item.tag
self.bind('<Button-3>', self.select)
self.bind('<B3-Motion>', self.drag)
self.bind('<Release-3>', self.drop)

The init method takes an Item as a parameter. It copies the attributes of the Item and then creates bindings for three events: a button press, button motion, and button release.

The event handler select stores the coordinates of the current event and the original color of the item, then changes the color to yellow:

def select(self, event):
self.dragx = event.x
self.dragy = event.y
 
self.fill = self.cget('fill')
self.config(fill='yellow')

cget stands for “get configuration;” it takes the name of an option as a string and returns the current value of that option.

drag computes how far the object has moved relative to the starting place, updates the stored coordinates, and then moves the item.

def drag(self, event):
dx = event.x - self.dragx
dy = event.y - self.dragy
 
self.dragx = event.x
self.dragy = event.y
 
self.move(dx, dy)

This computation is done in pixel coordinates; there is no need to convert to Canvas coordinates.

Finally, drop restores the original color of the item:

def drop(self, event):self.config(fill=self.fill)

You can use the Draggable class to add drag-and-drop capability to an existing item. For example, here is a modified version of make_circle that uses circle to create an Item and Draggable to make it draggable:

def make_circle(event):pos = ca.canvas_coords([event.x, event.y])item = ca.circle(pos, 5, fill='red')item = Draggable(item)

This example demonstrates one of the benefits of inheritance: you can modify the capabilities of a parent class without modifying its definition. This is particularly useful if you want to change behavior defined in a module you did not write.