This page intentionally left blank Python for Software Design



tải về 1.38 Mb.
Chế độ xem pdf
trang77/83
Chuyển đổi dữ liệu13.08.2023
Kích1.38 Mb.
#55046
1   ...   73   74   75   76   77   78   79   80   ...   83
- Python for Software Design How to Think Like a Computer Scientist-Cambridge University Press (2009)

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


224
Case Study: Tkinter
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 thinkpython.com/code/draggable_demo.py):
ca.bind('', 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 trans-
lates them to “Canvas coordinates,” which are compatible with Canvas methods like
circle
.
For Entry widgets, it is common to bind the  event, which is triggered when
the use 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('', 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


19.8 Binding
225
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 def-
inition for Draggable, which is a child class of Item that provides bindings that
implement drag-and-drop capability.
class Draggable(Item):
def __init__(self, item):
self.canvas = item.canvas
self.tag = item.tag
self.bind('', self.select)
self.bind('', self.drag)
self.bind('', 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


226
Case Study: Tkinter
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.
19.9
DEBUGGING
One of the challenges of GUI programming is keeping track of which things happen
while the GUI is being built and which things happen later in response to user events.
For example, when you are setting up a callback, it is a common error to call the
function rather than passing a reference to it:
def the_callback():
print 'Called.'
g.bu(text='This is wrong!', command=the_callback())
If you run this code, you will see that it calls the_callback immediately, and then
creates the button. When you press the button, it does nothing because the return
value from the_callback is None. Usually you do not want to invoke a callback
while you are setting up the GUI; it should only be invoked later in response to a
user event.


19.10 Glossary
227
Another challenge of GUI programming is that you don’t have control of the flow
of execution. Which parts of the program execute and their order are determined by
user actions. That means that you have to design your program to work correctly for
any possible sequence of events.
For example, the GUI in Exercise 19.3 has two widgets: one creates a Circle item
and the other changes the color of the Circle. If the user creates the circle and then
changes its color, there’s no problem. But what if the user changes the color of a
circle that doesn’t exist yet? Or creates more than one circle?
As the number of widgets grows, it is increasingly difficult to imagine all possible
sequences of events. One way to manage this complexity is to encapsulate the state
of the system in an object and then consider:

What are the possible states? In the Circle example, we might consider two states:
before and after the user creates the first circle.

In each state, what events can occur? In the example, the user can press either of
the buttons, or quit.

For each state-event pair, what is the desired outcome? Since there are two states
and two buttons, there are four state-event pairs to consider.

What can cause a transition from one state to another? In this case, there is a
transition when the user creates the first circle.
You might also find it useful to define, and check, invariants that should hold
regardless of the sequence of events.
This approach to GUI programming can help you write correct code without taking
the time to test every possible sequence of user events!

tải về 1.38 Mb.

Chia sẻ với bạn bè của bạn:
1   ...   73   74   75   76   77   78   79   80   ...   83




Cơ sở dữ liệu được bảo vệ bởi bản quyền ©hocday.com 2024
được sử dụng cho việc quản lý

    Quê hương