官术网_书友最值得收藏!

Events and callbacks – adding life to programs

Now that you have learned how to add widgets to a screen and position them where you want, let's turn our attention to the third component of GUI programming.

This addresses the question of how to make widgets functional.

Making widgets functional involves making them responsive to events such as the pressing of buttons, the pressing of keys on a keyboard, mouse clicks, and the like. This requires associating callbacks with specific events.

Callbacks are normally associated with specific widget events using the command binding rules, which is discussed in the following section.

Command binding

The simplest way to add functionality to a button is called command binding, whereby a callback function is mentioned in the form of command = some_callback in the widget option. Note that the command option is available only for a few selected widgets.

Take a look at the following sample code:

def my_callback ():
  # do something when button is clicked

After defining the above callback we can connect it to, say, a button with the command option referring to the callback, as follows:

  Button(root, text="Click me", command=my_callback)

A callback is a function memory reference (my_callback in the preceding example) that is called by another function (which is Button in the preceding example), which takes the first function as a parameter. Put simply, a callback is a function that you provide to another function so that it can call it.

Note that my_callback is passed without parentheses () from within the widget command option, because when the callback functions are set, it is necessary to pass a reference to a function rather than actually call it.

If you add parentheses () like you do for any normal function, it would be called as soon as the program runs. In contrast, the callback is called only when an event occurs (the pressing of a button in this case).

Passing arguments to callbacks

If a callback does not take any argument, it can be handled with a simple function, such as the one shown in the preceding code. However, if a callback needs to take arguments, we can use the lambda function, as shown in the following code snippet:

def my_callback (argument)
  #do something with argument

Then, somewhere else in the code, we define a button with a command callback that takes some arguments, as follows:

  Button(root,text="Click", command=lambda: my_callback ('some argument'))

Python borrows syntax from functional programming called the lambda function. The lambda function lets you define a single-line, nameless function on the fly.

The format for using lambda is as follows:

lambda arg: #do something with arg in a single line

Here's an example:

square = lambda x: x**2

Now, we can call the square method, as follows:

>>> print(square(5)) ## prints 25 to the console

Limitations of the command option

The command option that is available with the Button widget and a few other widgets is a function that can make the programming of a click-of-a-button event easy. Many other widgets do not provide an equivalent command binding option.

By default, the command button binds to the left-click and the space bar. It does not bind to the return key. Therefore, if you bind a button by using the command function, it will react to the space bar and not the return key. This is counter-intuitive for many users. What's worse is that you cannot change the binding of the command function easily. The moral is that the command binding, though a very handy tool, is not flexible enough when it comes to deciding your own bindings.

Event binding

Fortunately, Tkinter provides an alternative form of an event binding mechanism called bind() to let you deal with different events. The standard syntax used to bind an event is as follows:

widget.bind(event, handler, add=None)

When an event corresponding to the event description occurs in the widget, it calls not only the associated handler that passes an instance of the event object as the argument, but also the details of the event. If there already exists a binding for that event for this widget, the old callback is usually replaced with the new handler, but you can trigger both the callbacks by passing add='+' as the last argument.

Let's look at an example of the bind() method (refer to the 1.09.py code file):

from tkinter import *
root = Tk()
Label(root, text='Click at different\n locations in the frame below').pack()
def callback(event):
 print dir(event)
 print "you clicked at", event.x, event.y
frame = Frame(root, bg='khaki', width=130, height=80)
frame.bind("<Button-1>", callback)
frame.pack()
root.mainloop()

The following is a description of the preceding code:

  • We bind the Frame widget to the <Button-1> event, which corresponds to the left-click. When this event occurs, it calls the callback function, passing an object instance as its argument.
  • We define the callback(event) function. Note that it takes the event object generated by the event as an argument.
  • We inspect the event object by using dir(event), which returns a sorted list of attribute names for the event object passed to it. This prints the following list:

    ['__doc__', '__module__', 'char', 'delta', 'height', 'keycode', 'keysym', 'keysym_num', 'num', 'send_event', 'serial', 'state', 'time', 'type', 'widget', 'width', 'x', 'x_root', 'y', 'y_root']

  • From the attributes list generated by the object, we use two attributes, event.x and event.y, to print the coordinates of the point of click.

When you run the preceding code (code 1.09.py), it produces a window, as shown in following screenshot:

When you left-click anywhere in the yellow-colored frame within the root window, it outputs messages to the console. A sample message passed to the console is as follows:

['__doc__', '__module__', 'char', 'delta', 'height', 'keycode', 'keysym', 'keysym_num', 'num', 'send_event', 'serial', 'state', 'time', 'type', 'widget', 'width', 'x', 'x_root', 'y', 'y_root']
You clicked at 63 36.

Event patterns

In the previous example, you learned how to use the <Button-1> event to denote a left-click. This is a built-in pattern in Tkinter that maps it to a left-click event. Tkinter has an exhaustive mapping scheme that perfectly identifies events such as this one.

Here are some examples to give you an idea of event patterns:

In general, the mapping pattern takes the following form:

<[event modifier-]...event type [-event detail]>

Typically, an event pattern will comprise the following:

  • An event type: Some common event types include Button, ButtonRelease, KeyRelease, Keypress, FocusIn, FocusOut, Leave (when the mouse leaves the widget), and MouseWheel. For a complete list of event types, refer to the The event types section at http://www.tcl.tk/man/tcl8.6/TkCmd/bind.htm#M7.
  • An event modifier (optional): Some common event modifiers include Alt, Any (used like <Any-KeyPress>), Control, Double (used like <Double-Button-1> to denote a double-click of the left mouse button), Lock, and Shift. For a complete list of event modifiers, refer to the The event modifiers section at http://www.tcl.tk/man/tcl8.6/TkCmd/bind.htm#M6.
  • The event detail (optional): The mouse event detail is captured by the number 1 for a left-click and the number 2 for a right-click. Similarly, each key press on the keyboard is either represented by the key letter itself (say, B in <KeyPress-B>) or by using a key symbol abbreviated as keysym. For example, the up arrow key on the keyboard is represented by the keysym value of KP_Up. For a complete keysym mapping, refer to https://www.tcl.tk/man/tcl8.6/TkCmd/bind.htm.

Let's take a look at a practical example of the event binding on widgets (refer to code 1.10.py for the complete working example).

The following is a modified snippet of code; it will give you an idea of the commonly used event bindings:

widget.bind("<Button-1>", callback)  #bind widget to left mouse click
widget.bind("<Button-2>", callback) # bind to right mouse click
widget.bind("<Return>", callback)# bind  to Return(Enter) Key
widget.bind("<FocusIn>", callback) #bind  to  Focus in Event
widget.bind("<KeyPress-A>", callback)# bind  to keypress A
widget.bind("<KeyPress-Caps_Lock>", callback)# bind to CapsLock keysym
widget.bind("<KeyPress-F1>", callback)# bind widget to F1 keysym
widget.bind("<KeyPress-KP_5>", callback)# bind to keypad number 5
widget.bind("<Motion>", callback) # bind to motion over widget
widget.bind("<Any-KeyPress>", callback) # bind to any keypress

Rather than binding an event to a particular widget, you can also bind it to the top, level window. The syntax remains the same except that now you call it on the root instance of the root window like root.bind().

The levels of binding

In the previous section, you had a look at how to bind an event to an instance of a widget. This can be called an instance-level binding.

However, there may be times when you need to bind events to an entire application. At times, you may want to bind an event to a particular class of widget. Tkinter provides the following levels of binding options for this:

  • Application-level binding: Application-level bindings let you use the same binding across all windows and widgets of an application as long as any one window of the application is in focus.

    The syntax for application-level bindings is as follows:

    widget.bind(event, callback, add=None)

    The typical usage pattern is as follows:

    root.bind_all('<F1>', show_help)

    An application-level binding here means that irrespective of the widget that is currently under focus, pressing the F1 key will always trigger the show_help callback as long as the application is in focus.

  • Class-level binding: You can also bind events at a particular class level. This is normally used to set the same behavior for all instances of a particular widget class.

    The syntax for class-level binding is as follows:

    w.bind_class(class_name, event, callback, add=None)

    The typical usage pattern is as follows:

    my_entry.bind_class('Entry', '<Control-V>', paste)

In the preceding example, all the entry widgets will be bound to the <Control-V> event, which will call a method named paste (event).

Tip

Event propagation

Most keyboard and mouse events occur at the operating system level. It propagates hierarchically upwards from the source of the event until it finds a window that has the corresponding binding. The event propagation does not stop there. It propagates itself upwards, looking for other bindings from other widgets, until it reaches the root window. If it does reach the root window and no bindings are discovered by it, the event is disregarded.

主站蜘蛛池模板: 济宁市| 马龙县| 英超| 灵川县| 剑川县| 项城市| 洛浦县| 衡山县| 无棣县| 修水县| 潜山县| 集安市| 屏边| 梁平县| 宁都县| 大城县| 邯郸县| 错那县| 应城市| 苏尼特右旗| 荃湾区| 台东市| 神木县| 赤水市| 易门县| 汕尾市| 阜宁县| 阜南县| 绍兴县| 昌平区| 永清县| 襄城县| 乌拉特前旗| 鄢陵县| 乌鲁木齐市| 汤原县| 偃师市| 措美县| 南召县| 资中县| 吉林市|