在本章中,我们将介绍以下配方:
- 构造 Tkinter 应用程序
- 使用按钮
- 创建文本条目
- 跟踪文本更改
- 验证文本条目
- 选择数值
- 使用单选按钮创建选择
- 使用复选框实现交换机
- 显示项目列表
- 处理鼠标和键盘事件
- 设置主窗口的图标、标题和大小
由于其清晰的语法和广泛的库和工具生态系统,Python 已经成为一种流行的通用编程语言。从 web 开发到自然语言处理(NLP),您可以很容易地找到一个适合您的应用领域需要的开源库,在最后一个例子中,您可以始终使用 Python 标准库中包含的任何模块。
标准库遵循“包含电池”的理念,这意味着它包含大量实用程序:正则表达式、数学函数、网络等等。该库的标准图形用户界面(GUI包是Tkinter,是 Tcl/Tk 之上的一个薄的面向对象层。
从 Python 3 开始,Tkinter模块被重命名为tkinter(小写为t。它还影响到tkinter.ttk和tkinter.tix扩展。我们将在本书的最后一章深入探讨tkinter.ttk模块,因为tkinter.tix模块已被正式弃用。
在本章中,我们将探讨tkinter模块的一些基本类的几种模式,以及所有小部件子类通用的一些方法。
使用 Tkinter 制作应用程序的一个主要优点是,用几行脚本就可以很容易地建立一个基本的 GUI。随着程序变得越来越复杂,从逻辑上分离每个部分变得越来越困难,因此有组织的结构将帮助我们保持代码的整洁。
我们将以以下程序为例:
from tkinter import *
root = Tk()
btn = Button(root, text="Click me!")
btn.config(command=lambda: print("Hello, Tkinter!"))
btn.pack(padx=120, pady=30)
root.title("My Tkinter app")
root.mainloop()它创建了一个主窗口,其中有一个按钮,每次单击时都会在控制台中打印Hello, Tkinter!。按钮在水平轴上的填充为 120px,在垂直轴上的填充为 30px。最后一条语句启动主循环,主循环处理用户事件并更新 GUI,直到主窗口被破坏:
您可以执行该程序并验证它是否按预期工作。然而,我们所有的变量都是在全局名称空间中定义的,添加的小部件越多,就越难对使用它们的部分进行推理。
Wildcard imports (from ... import *) are strongly discouraged in production code because they pollute your global namespace—we only used them here to illustrate an anti-pattern that can be commonly seen in online examples.
这些可维护性问题可以通过基本的 OOP 技术来解决,这在所有类型的 Python 程序中都被认为是良好的实践。
为了提高我们的简单程序的模块性,我们将定义一个封装全局变量的类:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.btn = tk.Button(self, text="Click me!",
command=self.say_hello)
self.btn.pack(padx=120, pady=30)
def say_hello(self):
print("Hello, Tkinter!")
if __name__ == "__main__":
app = App()
app.title("My Tkinter app")
app.mainloop()现在,每个变量都包含在特定的范围内,包括作为单独方法移动的command函数。
首先,我们用import ... as语法替换了通配符导入,以便更好地控制全局名称空间。
然后,我们将App类定义为Tk子类,现在通过tk名称空间引用该子类。为了正确初始化基类,我们将使用内置的super()函数调用Tk类的__init__方法。这对应于以下行:
class App(tk.Tk):
def __init__(self):
super().__init__()
# ... 现在,我们有一个带有self变量的App实例引用,因此我们将添加所有按钮小部件作为类的属性。
尽管对于这样一个简单的程序来说,这看起来有些过分,但这种重构将帮助我们对每个部分进行推理,按钮实例化与单击时执行的回调分离,应用程序引导被移动到if __name__ == "__main__"块,这是可执行 Python 脚本中的常见做法。
我们将在所有代码示例中遵循此约定,因此您可以将此模板作为任何大型应用程序的起点。
在我们的示例中,我们对Tk类进行了子类化,但对其他小部件类进行子类化也是很常见的。我们这样做是为了重现重构代码之前的相同语句。
然而,在较大的程序中,例如那些有多个窗口的程序中,子类Frame或Toplevel可能更方便。这是因为 Tkinter 应用程序应该只有一个Tk实例,如果您在创建Tk实例之前实例化了一个小部件,系统会自动创建一个实例。
请记住,这个决定不会影响我们的App类的结构,因为所有小部件类都有一个mainloop方法在内部启动Tk主循环。
按钮小部件表示 GUI 应用程序的可单击项。它们通常使用文本或图像来指示单击时将执行的操作。Tkinter 允许您使用Button小部件类的一些标准选项轻松配置此功能。
以下内容包含一个按钮,该按钮的图像在单击时被禁用,以及一个按钮列表,其中包含不同类型的可用浮雕:
import tkinter as tk
RELIEFS = [tk.SUNKEN, tk.RAISED, tk.GROOVE, tk.RIDGE, tk.FLAT]
class ButtonsApp(tk.Tk):
def __init__(self):
super().__init__()
self.img = tk.PhotoImage(file="python.gif")
self.btn = tk.Button(self, text="Button with image",
image=self.img, compound=tk.LEFT,
command=self.disable_btn)
self.btns = [self.create_btn(r) for r in RELIEFS]
self.btn.pack()
for btn in self.btns:
btn.pack(padx=10, pady=10, side=tk.LEFT)
def create_btn(self, relief):
return tk.Button(self, text=relief, relief=relief)
def disable_btn(self):
self.btn.config(state=tk.DISABLED)
if __name__ == "__main__":
app = ButtonsApp()
app.mainloop()此程序的目的是显示创建按钮小部件时可以使用的几个配置选项。
执行上述代码后,您将获得以下输出:
Button实例化的最基本方式是使用text选项设置按钮标签,以及在点击按钮时引用要调用的函数的命令选项。
在 out 示例中,我们还通过image选项添加了PhotoImage,该选项优先于文本字符串。compound选项用于在同一按钮中组合图像和文本,确定图像的放置位置。它接受以下常量作为有效值:CENTER、BOTTOM、LEFT、RIGHT和TOP。
第二行按钮是使用RELIEF值列表创建的。每个按钮的标签对应于常量的名称,因此您可以注意到每个按钮外观的差异。
我们使用了一个属性来保留对PhotoImage实例的引用,即使我们没有在__init__方法之外使用它。原因是图像在垃圾收集时会被清除,如果我们将其声明为局部变量并且该方法存在,则会发生这种情况。
为了避免这种情况,请始终记住,只要显示的窗口仍处于活动状态,就要保留对每个PhotoImage对象的引用。
条目小部件表示一行中显示的文本输入。与Label和Button类一起,它是最常用的 Tkinter 类之一。
这个例子展示了如何创建一个登录表单,其中有两个输入实例用于username和password字段。password的每个字符都显示为星号,以避免以明文显示:
import tkinter as tk
class LoginApp(tk.Tk):
def __init__(self):
super().__init__()
self.username = tk.Entry(self)
self.password = tk.Entry(self, show="*")
self.login_btn = tk.Button(self, text="Log in",
command=self.print_login)
self.clear_btn = tk.Button(self, text="Clear",
command=self.clear_form)
self.username.pack()
self.password.pack()
self.login_btn.pack(fill=tk.BOTH)
self.clear_btn.pack(fill=tk.BOTH)
def print_login(self):
print("Username: {}".format(self.username.get()))
print("Password: {}".format(self.password.get()))
def clear_form(self):
self.username.delete(0, tk.END)
self.password.delete(0, tk.END)
self.username.focus_set()
if __name__ == "__main__":
app = LoginApp()
app.mainloop()Log in按钮打印控制台中的值,Clear按钮删除两个条目的内容,并将焦点返回到username条目:
条目小部件使用父窗口或框架作为第一个参数和一组可选的关键字参数来实例化,以配置其他选项。我们没有为username字段对应的条目指定任何选项。为了保密密码,我们使用字符串"*"指定show参数,该字符串将把每个键入的字符显示为星号。
使用get()方法,我们将以字符串的形式检索当前文本。这在print_login方法中用于在标准输出中显示条目的内容。
delete()方法采用两个参数,指示应删除字符的范围。请记住,索引从位置 0 开始,并且不包括范围末尾的字符。如果只传递一个参数,它将删除该位置的字符。
在clear_form()方法中,我们从索引 0 删除到常量END,这意味着整个内容被删除。最后,我们将焦点设置为username条目。
条目小部件的内容可以通过insert()方法以编程方式修改,该方法采用两个参数:
index:插入文本的位置;请注意,条目位置是 0 索引的string:要插入的文本
通过delete()和insert()的组合,可以实现用默认值重置条目内容的通用模式:
entry.delete(0, tk.END)
entry.insert(0, "default value") 另一种模式是将文本附加到文本光标的当前位置。在这里,您可以使用INSERT常数,而不必计算数值指数:
entry.insert(tk.INSERT, "cursor here")与Button类一样,Entry类也接受relief和state选项来修改其边框样式和状态。请记住,当状态为"disabled"或"readonly"时,对delete()和insert()的调用将被忽略。
- 跟踪文本更改配方
- 验证文本条目配方
Tk变量允许应用程序在输入更改其值时收到通知。Tkinter中有四个变量类:BooleanVar、DoubleVar、IntVar和StringVar。每一个都封装了相应 Python 类型的值,该值应该与附加到变量的输入小部件的类型相匹配。
如果希望根据某些输入小部件的当前状态自动更新应用程序的某些部分,则此功能特别有用。
在下面的示例中,我们将使用textvariable选项将StringVar实例关联到我们的条目;此变量使用show_message()方法作为回调跟踪写操作:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.var = tk.StringVar()
self.var.trace("w", self.show_message)
self.entry = tk.Entry(self, textvariable=self.var)
self.btn = tk.Button(self, text="Clear",
command=lambda: self.var.set(""))
self.label = tk.Label(self)
self.entry.pack()
self.btn.pack()
self.label.pack()
def show_message(self, *args):
value = self.var.get()
text = "Hello, {}!".format(value) if value else ""
self.label.config(text=text)
if __name__ == "__main__":
app = App()
app.mainloop() 当您在条目小部件中键入内容时,标签会用一条由Tk变量值组成的消息更新其文本。例如,如果您键入单词Phara,标签将显示Hello, Phara!。如果条目为空,标签将不显示任何文本。为了向您展示如何以编程方式修改变量的内容,我们添加了一个按钮,当您单击该项时,该按钮将清除该项:
应用程序构造函数的第一行实例化StringVar并将回调附加到写入模式。有效模式值如下所示:
"w":写入变量时调用"r":读取变量时调用"u"(对于未设置:删除变量时调用
调用时,回调函数接收三个参数:内部变量名、空字符串(用于其他类型的Tk变量)和触发操作的模式。通过使用*args声明方法,我们使这些参数成为可选的,因为我们在回调中没有使用任何这些值。
Tk包装器的get()方法返回变量的当前值,set()方法更新其值。它们还通知相应的观察者,因此通过 GUI 修改条目的内容或单击清除按钮都将触发对show_message()方法的调用。
Tk 变量对于Entry小部件是可选的,但对于其他小部件类(如Checkbutton和Radiobutton类)来说,它们是正常工作所必需的。
- 使用单选按钮创建选择
- 使用复选框实现开关配方
通常,文本输入表示遵循某些验证规则的字段,例如具有最大长度或匹配特定格式。有些应用程序允许在这些字段中键入任何类型的内容,并在提交整个表单时触发验证。
在某些情况下,我们希望防止用户在文本字段中键入无效内容。我们将了解如何使用 Entry 小部件的验证选项实现此行为。
以下应用程序演示如何使用正则表达式验证条目:
import re
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.pattern = re.compile("^\w{0,10}$")
self.label = tk.Label(self, text="Enter your username")
vcmd = (self.register(self.validate_username), "%i", "%P")
self.entry = tk.Entry(self, validate="key",
validatecommand=vcmd,
invalidcommand=self.print_error)
self.label.pack()
self.entry.pack(anchor=tk.W, padx=10, pady=10)
def validate_username(self, index, username):
print("Modification at index " + index)
return self.pattern.match(username) is not None
def print_error(self):
print("Invalid username character")
if __name__ == "__main__":
app = App()
app.mainloop() 如果运行此脚本并在 Entry 小部件中键入非字母数字字符,它将保留相同的内容并打印错误消息。当您尝试键入 10 个以上的有效字符时,也会发生这种情况,因为正则表达式也会限制内容的长度。
当validate选项设置为"key"时,我们将激活在任何内容修改时触发的条目验证。默认值为"none",表示没有验证。
其他可能的值是"focusin"和"focusout",它们分别在小部件获得或失去焦点时进行验证,或者在这两种情况下仅"focus"进行验证。或者,我们可以使用"all"值在所有情况下进行验证。
每次触发验证时调用validatecommand函数,如果新内容有效,则返回true,否则返回false。
因为我们需要更多信息来确定内容是否有效,所以我们使用Widget类的register方法围绕 Python 函数创建了一个 Tcl 包装器。然后,您可以为将传递给 Python 函数的每个参数添加替换百分比。最后,我们将这些值分组为 Python 元组。这对应于我们示例中的以下行:
vcmd = (self.register(self.validate_username), "%i", "%P") 通常,您可以使用以下任何替换:
%d:动作类型;1 表示插入,0 表示删除,否则为-1%i:正在插入或删除的字符串的索引%P:允许修改的分录值%s:修改前分录的值%S:正在插入或删除的字符串内容%v:当前设置的验证类型%V:触发操作的验证类型%W:输入小部件的名称
invalidcommand选项接受一个在validatecommand返回false时调用的函数。相同的百分比替换可以应用于该选项,但在我们的示例中,我们直接传递了类的print_error()方法。
Tcl/Tk 文档建议不要混合使用validatecommand和textvariable选项,因为为Tk变量设置无效值将关闭验证。如果validatecommand函数不返回布尔值,也会发生同样的情况。
如果您不熟悉re模块,您可以在的官方 Python 文档中查看正则表达式的详细介绍 https://docs.python.org/3.6/howto/regex.html 。
- 创建文本条目配方
以前的食谱包括如何使用文本输入;我们可能希望强制某些输入只包含数值。这是Spinbox和Scale类的用例。这两个小部件都允许用户从范围或有效选项列表中选择数值,但它们的显示和配置方式存在一些差异。
本程序有Spinbox和Scale用于选择0到5之间的整数值:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.spinbox = tk.Spinbox(self, from_=0, to=5)
self.scale = tk.Scale(self, from_=0, to=5,
orient=tk.HORIZONTAL)
self.btn = tk.Button(self, text="Print values",
command=self.print_values)
self.spinbox.pack()
self.scale.pack()
self.btn.pack()
def print_values(self):
print("Spinbox: {}".format(self.spinbox.get()))
print("Scale: {}".format(self.scale.get()))
if __name__ == "__main__":
app = App()
app.mainloop()在前面的代码中,出于调试目的,我们添加了一个按钮,当您单击每个小部件时,它会打印每个小部件的值:
这两个类都接受from_和to选项来指示有效值的范围。后面的下划线是必需的,因为from选项最初是在 Tcl/Tk 中定义的,但在 Python 中是保留关键字。
Scale类的一个方便功能是resolution选项,它设置舍入的精度。例如,分辨率为 0.2 将允许用户选择值 0.0、0.2、0.4 等等。此选项的值默认为 1,因此小部件将所有值舍入为最接近的整数。
通常,可以使用get()方法检索每个小部件的值。一个重要的区别是Spinbox以字符串形式返回数字,而Scale如果舍入接受十进制值,则返回整数值或浮点值。
Spinbox类的配置与条目小部件类似,例如textvariable和validate选项。您可以将所有这些模式应用于 Spinbox,其主要差异仅限于数值。
- 跟踪文本更改配方
使用 Radiobutton 小部件,您可以让用户在多个选项中进行选择。这种模式适用于数量相对较少的互斥选择。
您可以使用 Tkinter 变量连接多个Radiobutton实例,这样当您单击未选择的选项时,它将取消选择之前选择的任何其他选项。
在下面的程序中,我们为Red、Green和Blue选项创建了三个单选按钮。每次单击单选按钮时,它都会打印相应颜色的小写名称:
import tkinter as tk
COLORS = [("Red", "red"), ("Green", "green"), ("Blue", "blue")]
class ChoiceApp(tk.Tk):
def __init__(self):
super().__init__()
self.var = tk.StringVar()
self.var.set("red")
self.buttons = [self.create_radio(c) for c in COLORS]
for button in self.buttons:
button.pack(anchor=tk.W, padx=10, pady=5)
def create_radio(self, option):
text, value = option
return tk.Radiobutton(self, text=text, value=value,
command=self.print_option,
variable=self.var)
def print_option(self):
print(self.var.get())
if __name__ == "__main__":
app = ChoiceApp()
app.mainloop()如果运行此脚本,它将显示已选择红色单选按钮的应用程序:
为了避免重复Radiobutton初始化的代码,我们定义了一个从列表中调用的实用方法。我们解压缩了COLORS列表中每个元组的值,然后将这些局部变量作为选项传递给Radiobutton。记住尽可能不要重复你自己。
由于StringVar是在所有Radiobutton实例之间共享的,因此它们会自动连接,我们强制用户只选择一个选项。
我们在程序中设置了一个默认值"red";但是,如果我们省略这一行,StringVar的值与任何单选按钮值都不匹配,会发生什么?它将匹配tristatevalue选项的默认值,即空字符串。这会导致小部件以特殊的“三态”或不确定模式显示。虽然可以使用config()方法修改此选项,但更好的做法是设置一个合理的默认值,以便在有效状态下初始化变量。
两个备选方案之间的选择通常通过复选框和选项列表实现,其中每个选项独立于其他选项。正如我们将在下一个示例中看到的,这些概念可以使用 Checkbutton 小部件实现。
下面的应用程序显示了如何创建 Checkbutton,它必须连接到一个IntVar变量才能检查按钮状态:
import tkinter as tk
class SwitchApp(tk.Tk):
def __init__(self):
super().__init__()
self.var = tk.IntVar()
self.cb = tk.Checkbutton(self, text="Active?",
variable=self.var,
command=self.print_value)
self.cb.pack()
def print_value(self):
print(self.var.get())
if __name__ == "__main__":
app = SwitchApp()
app.mainloop() 在前面的代码中,我们只是在每次单击小部件时打印它的值:
与按钮小部件一样,Checkbutton 也接受command和text选项。
通过onvalue和offvalue选项,我们可以指定按钮打开和关闭时使用的值。我们使用整数变量,因为默认情况下,这些值分别是1和0;但是,也可以将它们设置为任何其他整数值。
通过复选按钮,还可以使用其他变量类型:
var = tk.StringVar()
var.set("OFF")
checkbutton_active = tk.Checkbutton(master, text="Active?", variable=self.var,
onvalue="ON", offvalue="OFF",
command=update_value)唯一的限制是将onvalue和offvalue与 Tkinter 变量的类型匹配;在这种情况下,因为"ON"和"OFF"是字符串,所以变量应该是StringVar。否则,Tcl 解释器将在尝试设置不同类型的相应值时引发错误。
- 跟踪文本更改配方
- 使用单选按钮创建选择
Listbox 小部件包含用户可以用鼠标或键盘选择的文本项。此选择可以是单个的,也可以是多个,具体取决于小部件配置。
下面的程序将创建一个包含一周中几天的列表选择。有一个按钮用于打印实际选择,还有一个按钮列表用于更改选择模式:
import tkinter as tk
DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday"]
MODES = [tk.SINGLE, tk.BROWSE, tk.MULTIPLE, tk.EXTENDED]
class ListApp(tk.Tk):
def __init__(self):
super().__init__()
self.list = tk.Listbox(self)
self.list.insert(0, *DAYS)
self.print_btn = tk.Button(self, text="Print selection",
command=self.print_selection)
self.btns = [self.create_btn(m) for m in MODES]
self.list.pack()
self.print_btn.pack(fill=tk.BOTH)
for btn in self.btns:
btn.pack(side=tk.LEFT)
def create_btn(self, mode):
cmd = lambda: self.list.config(selectmode=mode)
return tk.Button(self, command=cmd,
text=mode.capitalize())
def print_selection(self):
selection = self.list.curselection()
print([self.list.get(i) for i in selection])
if __name__ == "__main__":
app = ListApp()
app.mainloop() 您可以尝试更改选择模式并打印所选项目:
我们创建一个空的 Listbox 对象,并使用insert()方法添加所有文本项。0 索引表示应将项目添加到列表的开头。在下面的代码片段中,我们解包了DAYS列表,但单个项目可以在末尾附加END常量:
self.list.insert(tk.END, "New item") 使用curselection()方法检索当前选择。它返回所选项目的索引,将它们转换为对应的文本项目,我们称之为理解列表中每个索引的get()方法。最后,出于调试目的,该列表将打印在标准输出中。
在我们的示例中,selectmode选项可以通过编程方式更改,以探索不同的行为,如下所示:
SINGLE:单选BROWSE:单选,可通过上下键移动MULTIPLE:多选EXTENDED:多选,范围可通过Shift和Ctrl键选择
如果文本项的数量足够大,则可能需要添加垂直滚动条。您可以使用yscrollcommand选项轻松连接。在我们的示例中,我们可以将两个小部件包装在一个框架中,以保持相同的布局。请记住在包装卷轴时指定fill选项,以便填充y轴中的可用空间:
def __init__(self):
self.frame = tk.Frame(self)
self.scroll = tk.Scrollbar(self.frame, orient=tk.VERTICAL)
self.list = tk.Listbox(self.frame, yscrollcommand=self.scroll.set)
self.scroll.config(command=self.list.yview)
# ...
self.frame.pack()
self.list.pack(side=tk.LEFT)
self.scroll.pack(side=tk.LEFT, fill=tk.Y) 同样,水平轴也有一个xscrollcommand选项。
- 使用单选按钮创建选择
能够对事件做出反应是 GUI 应用程序开发中最基本但最重要的主题之一,因为它决定了用户如何与程序交互。
按键盘键和用鼠标单击项目是一些常见的事件类型,在某些 Tkinter 类中会自动处理这些事件。例如,这个行为已经在Button小部件类的command选项上实现,该选项调用指定的回调函数。
一些事件可以在没有用户交互的情况下触发,例如以编程方式将输入焦点从一个小部件更改为另一个小部件。
您可以使用bind方法将事件绑定附加到小部件。以下示例将一些鼠标事件绑定到Frame实例:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
frame = tk.Frame(self, bg="green",
height=100, width=100)
frame.bind("<Button-1>", self.print_event)
frame.bind("<Double-Button-1>", self.print_event)
frame.bind("<ButtonRelease-1>", self.print_event)
frame.bind("<B1-Motion>", self.print_event)
frame.bind("<Enter>", self.print_event)
frame.bind("<Leave>", self.print_event)
frame.pack(padx=50, pady=50)
def print_event(self, event):
position = "(x={}, y={})".format(event.x, event.y)
print(event.type, "event", position)
if __name__ == "__main__":
app = App()
app.mainloop() 所有事件都由我们类的print_event()方法处理,该方法在控制台中打印事件类型和鼠标位置。您可以用鼠标单击绿色框架,在它开始打印事件消息的同时移动它来尝试。
下面的示例包含一个带有两个绑定的条目小部件;一个用于在条目获得焦点时触发的事件,另一个用于所有按键事件:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
entry = tk.Entry(self)
entry.bind("<FocusIn>", self.print_type)
entry.bind("<Key>", self.print_key)
entry.pack(padx=20, pady=20)
def print_type(self, event):
print(event.type)
def print_key(self, event):
args = event.keysym, event.keycode, event.char
print("Symbol: {}, Code: {}, Char: {}".format(*args))
if __name__ == "__main__":
app = App()
app.mainloop() 当您在 Entry 小部件上设置焦点时,该程序将输出的第一条消息是FocusIn事件。如果您试用它,您将看到它还将显示与不可打印字符不对应的键的事件,例如箭头键或返回键。
bind方法在widget类中定义,有三个参数,一个事件sequence、一个callback函数和一个可选的add字符串:
widget.bind(sequence, callback, add='') sequence字符串使用<modifier-type-detail>语法。
首先,修饰符是可选的,允许您为事件的常规类型指定其他组合:
Shift:用户按下Shift键时Alt:当用户按下Alt键时Control:当用户按下Ctrl键时Lock:用户按下档锁时Double:事件快速连续发生两次时Triple:事件连续三次快速发生时
事件类型决定事件的一般类型:
ButtonPress或Button:按下鼠标按钮时生成的事件ButtonRelease:释放鼠标按钮时生成的事件Enter:将鼠标移到小部件上时生成的事件Leave:鼠标指针离开小部件时生成的事件FocusIn:小部件获取输入焦点时生成的事件FocusOut:小部件失去输入焦点时生成的事件KeyPress或Key:按键时产生的事件KeyRelease:按键释放时生成的事件Motion:鼠标移动时生成的事件
该细节也是可选的,用于指示鼠标按钮或键:
- 对于鼠标事件,1 是左键,2 是中键,3 是右键。
- 对于键盘事件,它是关键字符。特殊钥匙使用钥匙符号;常见的例子有返回键、制表符、Esc、上、下、右、左、退格和功能键(从F1到F12)。
callback函数采用事件参数。对于鼠标事件,它具有以下属性:
x和y:以像素为单位的当前鼠标位置x_root和y_root:与x和y相同,但相对于屏幕左上角num:鼠标按钮编号
对于键盘事件,它包含以下属性:
char:按字符串形式的字符代码keysym:按键符号keycode:按键编码
在这两种情况下,事件都具有引用生成事件的实例的widget属性和指定事件类型的type属性。
We strongly recommend that you define methods for the callback functions since you will also have the reference to the class instance, and therefore you can easily access each of the widget attributes.
最后,add参数可以是'',如果之前有绑定,则替换callback函数,或者'+'添加回调并保留旧的回调。
除了这里描述的事件类型外,还有其他类型在某些场景中可能有用,例如当小部件被破坏时生成的<Destroy>事件,或者当小部件的大小或位置改变时发送的<Configure>事件。
您可以在查看 Tcl/Tk 文档以获取事件类型的完整列表 https://www.tcl.tk/man/tcl/TkCmd/bind.htm#M7 。
- 构造 Tkinter 应用程序配方
Tk实例在配置方式上与普通小部件不同,因此我们将探讨一些基本方法,这些方法允许我们自定义它的显示方式。
此代码段创建了一个带有自定义标题和图标的主窗口。它的宽度为 400px,高度为 200px,屏幕左上角每个轴的间距为 10px:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("My Tkinter app")
self.iconbitmap("python.ico")
self.geometry("400x200+10+10")
if __name__ == "__main__":
app = App()
app.mainloop()此程序假定您在放置和执行脚本的同一目录中有一个名为python.ico的有效 ICO 文件。
Tk类的方法title()和iconbitmap()都是非常自描述性的。第一种方法设置窗口标题,而第二种方法则采用与窗口关联的图标路径。
geometry()方法使用以下模式的字符串配置窗口的大小:
{width}x{height}+{offset_x}+{offset_y}
如果在应用程序中添加更多的辅助窗口,Toplevel类中也提供了这些方法。
如果要使应用程序全屏显示,请将对geometry()方法的调用替换为self.state("zoomed")。







