6.事件和绑定
正如前几章提到的,Tkinter应用程序大部分事件都在事件循环中(通过mainloop方法进入事件循环)
事件来自于多个来源,比如用户的键盘的输入和鼠标操作,和window manager的重绘事件(大多数情况下不是有用户直接调用的)
Tkinter提供强大的机制让您自己处理事件,每个组件你都可以为各种事件绑定python的函数和方法
widget.bind(event, handler)
如果组件中发生了与event描述匹配的事件,将调用handler指定的处理程序,
例子,在窗口中捕获点击次数
from Tkinter import *
root = Tk()
def callback(event):
print "clicked at", event.x, event.y
frame = Frame(root, width=100, height=100)
frame.bind("
frame.pack()
root.mainloop()
在这里例子中,我们用bind方法把frame的鼠标左键点击事件绑定到callback函数
当控件拥有键盘焦点后,键盘事件将发送到对应的控件,你可以使用focus_set
方法来获得键盘焦点,获取键盘事件的代码:
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.y
frame = Frame(root, width=100, height=100)
frame.bind("
frame.bind("
frame.pack()
root.mainloop()
事件
事件用格式化的字符串来表示
type区域是事件说明符中最重要的部分,它说明我们想绑定哪一类的事件
例如Button\Key或者如进入、配置之类的窗口管理事件。Modifier和detail区域用于额外的信息
在许多情况下可以省略,还有各种方法来简化事件字符串,例如为了匹配键盘键,您可以省略
尖括号,只需使用键,当然要包含在尖括号内让我们来看看最常见的事件格式
事件格式
当您在组件上按下鼠标左键,随后的鼠标事件(移动、释放)就会发送到当前组件,即使
鼠标移动到当前组件之外,鼠标指针的的当前位置在传递给回调函数的event对象的x,y中
注意,如果你绑定了
Delete\F1\F5\Num_Lock都可以
frame1.bind("
Event对象
event对象是标准的Python对象实例,有许多属性
事件属性
widget:产生这个事件的组件,这个一个合法Tkinter组件实例而不是名字,所有的事件都有这个属性
x,y:当前鼠标的位置,像素为单位
x_root,y_root:当前鼠标相对于上层框架的位置
char:字符码 只有键盘事件才有,string类型
keysym:键盘符号只有键盘事件才有
keycode:键盘代码
num:鼠标的编号,左键1,中键2,右键3....
width,height:组件新尺寸,只有Configure 事件才有,像素
type:事件类型
实例和类的绑定
以上的例子中我们用的都是在实例上使用bind方法,这意味着这样只能bind在一个组件上,
如果我们创建一个新的组件,他们不会继承这些绑定关系。
不过Tkinter也提供了类级别和应用级别的bind,实际上,你可以使用以下级别的binding
1.组件实例
2.组件的顶层窗体(Toplevel 或者 root)
3.组件类,用bind_class方法
4.整个应用,用bind_all方法
比如,你可以用bind_all来绑定F1按钮的点击,这样你能在这个应用的如何地方点击都可以弹出帮助框
但如果同一个键你在多处绑定了怎么办?
首先,在以上4个层次之内,Tkinter选择最接近匹配的方式。比如为
和
但是,如果你如果在以上4个层次间,比如你同时向toplevel组件添加
Tkinter首先调用实例级别的最佳绑定,最后在应用程序级别调用最佳可用绑定,因此,
在极端情况下,单个事件可以调用4个事件处理程序。
常见的混乱原因是当您尝试使用绑定来覆盖标准组件的默认行为。例如,假设你想在
文本框内禁止输入回车键,这样用户就无法输入多行数据,也行你会用下面的小伎俩
def ignore(event):
pass
text.bind("
或者,你喜欢一行的简洁代码
text.bind("
不幸的是,新的一行依然会插入,因为,以上的绑定仅仅应用在应用级别,
而标准的行为依然有类级别的绑定实现了。
你可以使用bind_class方法来改变类级别的绑定,但这将更改应用程序中所有文本组件的行为。下面是比较合理的解决办法
def ignore(event):
return "break"
text.bind("
顺便说一句,如果你真的想改变所有文本组件的默认行为,你可以用以下bind_class方法
top.bind_class("Text", "
真的不建议这么做,不要改变组件的默认行为。
协议
除了事件绑定,Tkinter还提供了协议处理的机制,这里的协议指的是应用程序和windows manager之间的互动
最常见的是WM_DELETE_WINDOW,用于定义当用户使用窗口管理器显式关闭窗口是的事件。
你可以用protocol方法来安装这个协议的回调函数(这个组件必须是root或者Toplevel组件)
widget.protocol("WM_DELETE_WINDOW", handler)
一旦你注册了自己的处理函数,Tkinter将不再自动的关闭程序,比如下面这个例子
Capturing destroy events
from Tkinter import *
import tkMessageBox
def callback():
if tkMessageBox.askokcancel("Quit", "Do you really wish to quit?"):
root.destroy()
root = Tk()
root.protocol("WM_DELETE_WINDOW", callback)
root.mainloop()
注意,即使你没有在顶层窗口注册WM_DELETE_WINDOW的处理程序,窗口还是会被销毁的。最好还是自己注册一个处理程序
top = Toplevel(...)#确保窗口小部件实例被删除
top.protocol(“WM_DELETE_WINDOW”,top.destroy)