在使用Tkinter库实现GUI开发的过程中,属性和方法是Tkinter控件的两个重要元素。但除此之外,还需要借助事件来实现Tkinter控件的动态功能效果。例如,在窗口中创建一个文件菜单,单击“文件”菜单后应该打开一个“选择文件”对话框,只有这样才是一个合格的软件。这个单击“文件”菜单就打开一个“选择文件”对话框的过程是通过单击事件完成的。由此可见,在计算机控件应用中,事件就是执行某个功能的动作。本节将详细讲解库Tkinter中常用事件的基本知识。
在计算机系统中有很多种事件,如鼠标事件、键盘事件和窗口事件等。鼠标事件主要是指鼠标按键的按下、释放,鼠标滚轮的滚动,鼠标指针移进、移出组件等所触发的事件。键盘事件主要是指键的按下、释放等所触发的事件。窗口事件是指改变窗口大小、组件状态等所触发的事件。
在Tkinter库中,事件是指在各个组件上发生的各种鼠标事件和键盘事件。对于按钮组件、菜单组件来说,可以在创建组件时通过参数command指定其事件的处理函数。除组件所触发的事件之外,在创建右键弹出菜单时还需处理右击事件。类似的事件还有鼠标事件、键盘事件和窗口事件。
在Python程序的Tkinter库中,鼠标事件、键盘事件和窗口事件可以采用事件绑定的方法来处理消息。为了实现控件绑定功能,可以使用控件中的bind()方法实现,或者使用bind_class()方法实现类绑定,分别调用函数或者类来响应事件。bind_all()方法也可以绑定事件,bind_all()方法能够将所有的组件事件绑定到事件响应函数上。上述3个方法的具体语法格式如下。
bind(sequence, func, add)
bind_class( className, sequence, func, add)
bind_all (sequence, func, add)
各个参数的具体说明如下。
● func:表示所绑定的事件处理函数。
● add:表示可选参数,为空字符或者“+”。
● className:表示所绑定的类。
● sequence:表示所绑定的事件,必须是以尖括号“<>”包围的字符串。
在Tkinter库中,常用的鼠标事件如下表所示:
| 名称 | 描述 |
|---|---|
| < Button-1 > | 表示按下鼠标左键,而< Button-2 >表示按下鼠标中键,< Button-3 >表示按下鼠标右键。 |
| < ButtonPress-1 > | 表示按下鼠标左键,与< Button-1 >相同。 |
| < ButtonRelease-1 > | 表示释放鼠标左键。 |
| < B1-Motion > | 表示按住鼠标左键并移动。 |
| < Double-Button-1 > | 表示双击鼠标左键。 |
| < Enter > | 表示鼠标指针进入某一组件区域。 |
| < Leave > | 表示鼠标指针离开某一组件区域。 |
| < MouseWheel > | 表示鼠标滑轮滚动动作。 |
在上述鼠标事件中,数字“1”可以替换成2或3。其中数字“2”表示鼠标中键,数字“3”表示鼠标右键。例如,< B3-Motion >表示按住鼠标右键并移动,< Double-Button-2 >表示双击鼠标中键等。
在Tkinter库中,常用的键盘事件如下表所示:
| 名称 | 描述 |
|---|---|
| < KeyPress-A > | 表示按下A键,可用其他字母键代替。 |
| < Alt-KeyPress-A > | 表示同时按下Alt键和A键。 |
| < Control-KeyPress-A > | 表示同时按下Control键和A键。 |
| < Shift-KeyPress-A > | 表示同时按下Shift键和A键。 |
| < Double-KeyPress-A > | 表示快速地按两下A键。 |
| < Lock-KeyPress-A > | 表示先按下CapsLock键再按下A键。 |
在上述键盘事件中,还可以使用Alt键、Control键和Shift的组合键。例如< Alt-Control-Shift- KeyPress-B >表示同时按下Alt键、Control键、Shift键和B键。其中,KeyPress可以使用KeyRelease替换,表示当按键释放时触发事件。在此需要注意的是,输入的字母要区分大小写,如果使用< KeyPress-A >,则只有按下Shift键或者按下CapsLock键时才可以触发事件。
在Tkinter库中,常用的窗口事件如下表所示:
| 名称 | 描述 |
|---|---|
| Activate | 当控件由不可用转为可用时触发。 |
| Configure | 当控件大小改变时触发。 |
| Deactivate | 当控件由可用转为不可用时触发。 |
| Destroy | 当控件被销毁时触发。 |
| Expose | 当控件从被遮挡状态中暴露出来时触发。 |
| FocusIn | 当控件获得焦点时触发。 |
| FocusOut | 当控件失去焦点时触发。 |
| Map | 当控件由隐藏状态变为显示状态时触发。 |
| Property | 当窗体的属性被删除或改变时触发。 |
| Unmap | 当控件由显示状态变为隐藏状态时触发。 |
| Visibility | 当控件变为可视状态时触发 |
当把窗口中的事件绑定到函数时,如果触发该事件,则会调用所绑定的函数进行处理。触发事件后,系统将向该函数传递一个event对象的参数。正因为如此,应该将被绑定的响应事件函数定义成如下格式。
def function (event):
<语句>
在上述格式中,event对象具有的属性信息如下表所示:
| 属性 | 功能 |
|---|---|
| char | 按键字符,仅对键盘事件有效 |
| keycode | 按键名,仅对键盘事件有效 |
| keysym | 按键编码,仅对键盘事件有效 |
| num | 鼠标按键,仅对鼠标事件有效 |
| type | 所触发的事件类型 |
| widget | 引起事件的控件 |
| width, height | 控件改变后的大小,仅对Configure有效 |
| x,y | 相对于窗口,鼠标当前的位置 |
| x_root, y_root | 相对于整个屏幕,鼠标当前的位置 |
下列实例代码演示了使用Tkinter创建一个“英尺/米”转换器的过程。
from tkinter import *
from tkinter import ttkdef calculate(*args):try:value = float(feet.get())meters.set(value * 1.8 + 32)except ValueError:pass
root = Tk()
root.title("温度换算")
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
feet = StringVar()
meters = StringVar()
feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet)
feet_entry.grid(column=2, row=1, sticky=(W, E))
ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E))
ttk.Button(mainframe, text="计算", command=calculate).grid(column=3, row=3, sticky=W)
ttk.Label(mainframe, text="摄氏度").grid(column=3, row=1, sticky=W)
ttk.Label(mainframe, text="相当于").grid(column=1, row=2, sticky=E)
ttk.Label(mainframe, text="华氏度").grid(column=3, row=2, sticky=W)
for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)
feet_entry.focus()
root.bind('', calculate)
root.mainloop()
上述代码的实现流程如下。
1)导入了Tkinter所有的模块,这样可以直接使用Tkinter的所有功能,这是Tkinter的标准做法。然而,在后面导入ttk后,接下来要用到的组件前面都要加前缀。举个例子,直接调用“Entry”会调用Tkinter内部的模块。然而,我们需要的是ttk里的“Entry”,所以,要使用“ttk.Enter”,如你所见,许多函数在两者中都有。如果同时用到这两个模块,则需要根据整体代码选择使用哪个模块,让ttk的调用更加清晰,本书也会使用这种风格。
2)创建主窗口,设置窗口的标题为“温度换算”。然后创建一个frame控件,用户界面上的所有东西都包含在里面,并且放在主窗口中。columnconfigure”/“rowconfigure用于告诉Tk,如果调整主窗口的大小,frame空间的大小也随之调整。
3)创建3个主要的控件,一个用来输入英尺的文本框,一个用来表示转换成米后的结果的标签,一个用于执行计算的“计算”按钮。这3个控件都是窗口中“带主题”控件的类的实例。同时为它们设置一些选项,比如输入的宽度、按钮显示的文本等。输入框和标签都带了一个神秘的参数“textvariable”。如果仅创建控件,控件是不会自动显示在屏幕上的,因为Tk并不知道这些控件和其他控件的位置关系。那是“grid”部分要做的事情。还记得程序的网格布局吗?我们把每个控件放到对应的行或者列中,“sticky”选项指明控件在网格单元中的排列,采用的是指南针方向。所以“w”代表固定这个控件在左边的网格中,“we”代表固定这个控件在左边的网格和右边的网格之间。
4)创建3个静态标签,然后放在合适的网格位置中。在最后4行代码中,首先处理frame中的所有控件,并且在每个控件的四周添加一些空隙。可以在之前调用grid的时候做这些事,但上面这样做也是一个不错的选择。然后告诉Tk让输入框获取到焦点。这个方法可以让光标一开始就在输入框的位置,用户可以不用再单击了。最后告诉Tk,如果用户按下了Enter键,就执行计算,等同于用户单击“计算”按钮。
在上述代码中定义了计算过程,无论是按Enter键还是单击“计算”按钮,都会从文本框中获取摄氏度,转换成华氏度,然后输出到标签中。执行“温度换算”的效果如下图所示。
