Python Tkinter 事件和映射

来源:互联网 发布:win7无法启动网络共享 编辑:程序博客网 时间:2024/06/05 14:14

Events and Bindings

As was mentioned earlier, a Tkinter application spends most of its time inside an event loop (entered via the mainloop method). Events can come from various sources, including key presses and mouse operations by the user, and redraw events from the window manager (indirectly caused by the user, in many cases).

就像前面所说的,一个Tkinter应用在绝大部分时间都花费在内部的时间循环上(通过mainloop方法进入)。事件来自于各种途径。包括来自用户的按键和鼠标操作,窗口管理器的刷新事件(大多数情况下由用户直接触发)。

Tkinter provides a powerful mechanism to let you deal with events yourself. For each widget, you can bind Python functions and methods to events.

Tkinter提供了一个有效的机制去处理事件。对于每一个组件,都可以把Python功能和方法映和事件映射在一起。

widget.bind(event, handler)

If an event matching the event description occurs in the widget, the given handler is called with an object describing the event.

Here’s a simple example:

如果在一个部件上发生的事件刚好个函数中event的描述相匹配,事先定义的handle将会被触发。

这里有一个简单的例子:

Capturing clicks in a window
from Tkinter import *root = Tk()def callback(event):    print "clicked at", event.x, event.yframe = Frame(root, width=100, height=100)frame.bind("<Button-1>", callback)frame.pack()root.mainloop()


In this example, we use the bind method of the frame widget to bind a callback function to an event called <Button-1>. Run this program and click in the window that appears. Each time you click, a message like “clicked at 44 63” is printed to the console window.

在这个例子里,使用了frame组件的bind方法去把一个callback函数和一个叫做<Button-1>的时间映射在一起。运行这个程序,并行在出现的窗口中点击,每点击一次,一条类似"clicked at 44 63"的信息就会被打印到控制台窗口。

Keyboard events are sent to the widget that currently owns the keyboard focus. You can use the focus_set method to move focus to a widget:

键盘事件将会被发送到当前拥有键盘焦点的组件中。focus_set方法可以把键盘焦点设置到一个组件上。

Capturing keyboard events
from Tkinter import *root = Tk()def key(event):    print "pressed", repr(event.char)def callback(event):    frame.focus_set()    print "clicked at", event.x, event.yframe = Frame(root, width=100, height=100)frame.bind("<Key>", key)frame.bind("<Button-1>", callback)frame.pack()root.mainloop()

If you run this script, you’ll find that you have to click in the frame before it starts receiving any keyboard events.

如果运行这个脚本,你会发现在它开始接收任何键盘事件之前必须先点击选中这个frame。

Events #

Events are given as strings, using a special event syntax:

事件就像字符串一样被赋值,使用一个特殊的时间语句:

<modifier-type-detail>

The type field is the most important part of an event specifier. It specifies the kind of event that we wish to bind, and can be user actions like Button, andKey, or window manager events like EnterConfigure, and others. The modifier and detail fields are used to give additional information, and can in many cases be left out. There are also various ways to simplify the event string; for example, to match a keyboard key, you can leave out the angle brackets and just use the key as is. Unless it is a space or an angle bracket, of course.

Type 部分是一个事件语句中最重要的部分,它指定了想要去映射的事件类型。它可以是用户动作如按钮或按键,或者窗口管理器事件如 Enter、Configure或者其他的。Modifier和detail 部分被用于添加附加信息,在很多情况下可以空白不写。有很多种方法去简化事件字符串,例如,在匹配一个键盘键的时候,可以不写尖括号而仅仅使用键本身,当然当它是一个空格或者尖括号的时候除外。

Instead of spending a few pages on discussing all the syntactic shortcuts, let’s take a look on the most common event formats:

Event Formats

事件格式
<Button-1>

A mouse button is pressed over the widget. Button 1 is the leftmost button, button 2 is the middle button (where available), and button 3 the rightmost button. When you press down a mouse button over a widget, Tkinter will automatically “grab” the mouse pointer, and subsequent mouse events (e.g. Motion and Release events) will then be sent to the current widget as long as the mouse button is held down, even if the mouse is moved outside the current widget. The current position of the mouse pointer (relative to the widget) is provided in the x and y members of the event object passed to the callback.

You can use ButtonPress instead of Button, or even leave it out completely: <Button-1><ButtonPress-1>, and <1> are all synonyms. For clarity, I prefer the <Button-1> syntax.

这个事件表示一个鼠标按键被按下。Button 1表示左键,button 2表示中键,Button 3表示右键。当在一个组件上按下一个鼠标键的时候Tkinter会自动的'grab'鼠标指针,当这个鼠标按键一直被持续按下,接下来的鼠标事件(比如移动和释放事件)将会被发送到组件上,甚至鼠标已经被移到了当前组件的外面。鼠标指针的当前位置(相对于组件)会在event对象中以成员x 和 y 的形式传递给callback。

你可以使用ButtonPress来替代Button,甚至不写:<Button-1><ButtonPress-1>和<1>是一样的意思。为了表达清晰,我更喜欢使用<Button-1>语句。


<B1-Motion>

The mouse is moved, with mouse button 1 being held down (use B2 for the middle button, B3 for the right button). The current position of the mouse pointer is provided in the x and y members of the event object passed to the callback.

当Button 1被按下的时候移动鼠标(B2代表中键,B3代表右键),鼠标指针的当前位置将会以event对象的x y 成员的形式传递给callback。

<ButtonRelease-1>

Button 1 was released. The current position of the mouse pointer is provided in the x and y members of the event object passed to the callback.

Button 1被释放。鼠标指针的当前位置将会以event对象的x y 成员的形式传递给callback。

<Double-Button-1>

Button 1 was double clicked. You can use Double or Triple as prefixes. Note that if you bind to both a single click (<Button-1>) and a double click, both bindings will be called.

Button 1被双击。可以使用Double 或者 Triple前缀。注意:如果你同时映射了一个单击和一个双击,两个映射都会被调用。

<Enter>

The mouse pointer entered the widget (this event doesn’t mean that the user pressed the Enter key!).

鼠标指针进入组件范围(这个事件不是用户按下了Enter键的意思)。

<Leave>

The mouse pointer left the widget.

鼠标指针离开组件范围。

<FocusIn>

Keyboard focus was moved to this widget, or to a child of this widget.

键盘焦点切换到这个组件或者子组件。

<FocusOut>

Keyboard focus was moved from this widget to another widget.

键盘焦点从一个组件切换到另外一个组件。

<Return>

The user pressed the Enter key. You can bind to virtually all keys on the keyboard. For an ordinary 102-key PC-style keyboard, the special keys are Cancel (the Break key), BackSpaceTabReturn(the Enter key), Shift_L (any Shift key), Control_L (any Control key),Alt_L (any Alt key), PauseCaps_LockEscapePrior (Page Up), Next (Page Down), EndHomeLeftUpRightDown,PrintInsertDeleteF1F2F3F4F5F6F7F8F9F10F11,F12Num_Lock, and Scroll_Lock.

用户按下Enter键。你可以映射键盘上所有的按键。对于一个普通的102键键盘,特殊按键有Cancel (the Break key), BackSpaceTabReturn(the Enter key), Shift_L (any Shift key), Control_L (any Control key),Alt_L (any Alt key), PauseCaps_LockEscapePrior (Page Up), Next (Page Down), EndHomeLeftUpRightDown,PrintInsertDeleteF1F2F3F4F5F6F7F8F9F10F11,F12Num_Lock, and Scroll_Lock.

<Key>

The user pressed any key. The key is provided in the char member of the event object passed to the callback (this is an empty string for special keys).

用户按下任何键。这个键会以event对象的char成员的形式传递给callback(对于特殊按键会是一个空字符串)

a

The user typed an “a”. Most printable characters can be used as is. The exceptions are space (<space>) and less than (<less>). Note that 1 is a keyboard binding, while <1> is a button binding.

用户输入‘a’。所有的可打印字符都可以这样使用。空格<space>和少于<less>例外。 注意 ‘1’表示映射键盘上的数字1,而<1>是一个鼠标映射

<Shift-Up>

The user pressed the Up arrow, while holding the Shift key pressed. You can use prefixes like AltShift, and Control.

用户在按住Shift键的同时,按下Up箭头。你可以使用Alt + Shift + Control一样的各种组合。

<Configure>

The widget changed size (or location, on some platforms). The new size is provided in the width and height attributes of the event object passed to the callback.

改变组件的形状(在某些平台上表示的是位置)。新形状以event对象中width 和 height属性的形式传递给callback。

The Event Object

The event object is a standard Python object instance, with a number of attributes describing the event.

事件对象是一个标准的Python对象类,拥有大量的属性去描述事件。

Event Attributes

事件属性
widget

The widget which generated this event. This is a valid Tkinter widget instance, not a name. This attribute is set for all events.

产生事件的组件。这是一个合法的Tkinter组件实例,而不是一个名字。所有的事件都归于此类。

x, y

The current mouse position, in pixels.

当前的鼠标位置,单位:像素。

x_root, y_root

The current mouse position relative to the upper left corner of the screen, in pixels.

当前鼠标位置相对于屏幕左上角的位置,单位:像素

char

The character code (keyboard events only), as a string.

字符代码(仅键盘事件)字符串的格式

keysym

The key symbol (keyboard events only).

按键符合(仅键盘事件)

keycode

The key code (keyboard events only).

按键代码(仅键盘事件)

num

The button number (mouse button events only).

按钮数字(仅鼠标按键事件)

width, height

The new size of the widget, in pixels (Configure events only).

组件的新形状(仅configure事件)

type

The event type.

事件type

For portability reasons, you should stick to charheightwidthxy,x_rooty_root, and widget. Unless you know exactly what you’re doing, of course…

为了便于理解,应该遵守charheightwidthxy,x_rooty_root, and widget. 除非你明确知道自己在做什么。

Instance and Class Bindings

The bind method we used in the above example creates an instance binding. This means that the binding applies to a single widget only; if you create new frames, they will not inherit the bindings.

上面例子中的bind 方法创建了一个实例映射。这就意味着这个映射只能用于一个单独的组件。如果创建了新的frame,他们并不会继承前面的映射。

But Tkinter also allows you to create bindings on the class and application level; in fact, you can create bindings on four different levels:

Tkinter允许在类和应用的层面创建映射。事实上可以在4个不同的层面创建映射:

  • the widget instance, using bind.

  • 组件实例,使用bind

  • the widget’s toplevel window (Toplevel or root), also using bind.

  • 组件的toplevel 窗口(Toplevel或者root),同样使用bind

  • the widget class, using bind_class (this is used by Tkinter to provide standard bindings).

  • 组件类,使用bind_class(被Tkinter用于提供标准映射)

  • the whole application, using bind_all.

  • 对于整个应用,使用bind_all

For example, you can use bind_all to create a binding for the F1 key, so you can provide help everywhere in the application. But what happens if you create multiple bindings for the same key, or provide overlapping bindings?

例如,可以使用bind_all为F1键创建一个映射,这样就可以在应用的任意位置使用F1调用帮助。但如果为同一个按键创建了多个映射,或者提供了重叠的映射,会发生什么呢?

First, on each of these four levels, Tkinter chooses the “closest match” of the available bindings. For example, if you create instance bindings for the<Key> and <Return> events, only the second binding will be called if you press the Enter key.

首先,在这4层的每一层,Tkinter会选择“最匹配”的可用映射。例如你为<key>和<Return>创建了实例映射,当你按下Enter键的时候只有第二个映射会被调用。

However, if you add a <Return> binding to the toplevel widget, both bindings will be called. Tkinter first calls the best binding on the instance level, then the best binding on the toplevel window level, then the best binding on the class level (which is often a standard binding), and finally the best available binding on the application level. So in an extreme case, a single event may call four event handlers.

如果在toplevel的组件添加了一个<Return>映射,所有的映射都会被调用。Tkinter会先在instance层调用最佳映射,然后是toplevel windows层,然后是类层,最后会是应用层。在一个极端的情况下,一个独立事件可以可以调用4个事件操作。

A common cause of confusion is when you try to use bindings to override the default behavior of a standard widget. For example, assume you wish to disable the Enter key in the text widget, so that the users cannot insert newlines into the text. Maybe the following will do the trick?

通常在尝试用映射去重载一个标准组件的默认行为的时候会引起混淆和困惑。例如:为了不让用户在文本框中输入新的行,想在一个文本框组件中禁用Enter键,下面的代码能完成任务吗?

def ignore(event):    passtext.bind("<Return>", ignore)

or, if you prefer one-liners:

或者一行:

text.bind("<Return>", lambda e: None)

(the lambda function used here takes one argument, and returns None)

(这里使用的lambda函数拥有一个参数,返回None)

Unfortunately, the newline is still inserted, since the above binding applies to the instance level only, and the standard behavior is provided by a class level bindings.

很不幸,依旧可以插入新行,因为上面的映射仅被用在instance层,而标准行为是由class level提供的。

You could use the bind_class method to modify the bindings on the class level, but that would change the behavior of all text widgets in the application. An easier solution is to prevent Tkinter from propagating the event to other handlers; just return the string “break” from your event handler:

可以使用bind_class方法在class层去修改映射,但这会改变这个应用里所有的文本框组件的行为。一个简单的方案是去阻止Tkinter把事件传递给其他句柄,仅仅从你的事件句柄返回字符串'break'

def ignore(event):    return "break"text.bind("<Return>", ignore)

or

text.bind("<Return>", lambda e: "break")

By the way, if you really want to change the behavior of all text widgets in your application, here’s how to use the bind_class method:

如果真的想改变应用程序中所有文本框组件的行为,这里介绍了如何使用bind_class方法:

top.bind_class("Text", "<Return>", lambda e: None)

But there are a lot of reasons why you shouldn’t do this. For example, it messes things up completely the day you wish to extend your application with some cool little UI component you downloaded from the net. Better use your own Text widget specialization, and keep Tkinter’s default bindings intact:

但有太多的原因不这样去做。比如它会把当天从网上下载下来的希望能扩展的应用的小UI部件全部搞乱。最好还是定制自己的Text组件并保持Tkinter默认映射的完整。

class MyText(Text):    def __init__(self, master, **kw):        apply(Text.__init__, (self, master), kw)        self.bind("<Return>", lambda e: "break")

Protocols #

In addition to event bindings, Tkinter also supports a mechanism called protocol handlers. Here, the term protocol refers to the interaction between the application and the window manager. The most commonly used protocol is called WM_DELETE_WINDOW, and is used to define what happens when the user explicitly closes a window using the window manager.

除了事情映射,Tkinter还支持协议句柄机制。这个协议适用于应用和窗口管理器之间的内部交互。最常用的协议叫做WM_DELETE_WINDOW,用于定义用户使用窗口管理器关闭一个窗口的动作。

You can use the protocol method to install a handler for this protocol (the widget must be a root or Toplevel widget):

使用protocol方法为这个协议安装一个句柄(这里的组件必须是root或Toplevel组件)

widget.protocol("WM_DELETE_WINDOW", handler)

Once you have installed your own handler, Tkinter will no longer automatically close the window. Instead, you could for example display a message box asking the user if the current data should be saved, or in some cases, simply ignore the request. To close the window from this handler, simply call the destroy method of the window:

一旦在自己的句柄中使用了这个协议,Tkinter就不会自动的关闭这个窗口。可以显示一个消息窗口去询问用户是否保存当前数据,或简单地忽略这个请求。调用当前窗口的destroy方法可以从句柄去关闭这个窗口

from Tkinter import *import tkMessageBoxdef callback():    if tkMessageBox.askokcancel("Quit", "Do you really wish to quit?"):        root.destroy()root = Tk()root.protocol("WM_DELETE_WINDOW", callback)root.mainloop()

Note that even you don’t register an handler for WM_DELETE_WINDOW on a toplevel window, the window itself will be destroyed as usual (in a controlled fashion, unlike X). However, as of Python 1.5.2, Tkinter will not destroy the corresponding widget instance hierarchy, so it is a good idea to always register a handler yourself:

注意即便没有在一个toplevel窗口为WM_DELETE_WINDOW注册一个句柄,这个窗口自己照样会销毁。截止到Python 1.5.2,Tkinter不会自动销毁相应的组件实例,自己注册一个句柄是个不错的注意

top = Toplevel(...)# make sure widget instances are deletedtop.protocol("WM_DELETE_WINDOW", top.destroy)

Future versions of Tkinter will most likely do this by default.

未来版本的Tkinter极有可能引入这个机制。

Other Protocols

其他协议

Window manager protocols were originally part of the X window system (they are defined in a document titled Inter-Client Communication Conventions Manual, or ICCCM). On that platform, you can install handlers for other protocols as well, like WM_TAKE_FOCUS and WM_SAVE_YOURSELF. See the ICCCM documentation for details.

窗口管理器协议起初是 X窗口系统的一部分(它是在一个名为Inter-Client Communication Conventions Manual, or ICCCM中定义的)。在这个平台上你可以安装其他的句柄比如WM_TAKE_FOCUS 和 WM_SAVE_YOURSELF. 具体可以参考ICCCM。

1 0
原创粉丝点击