tkiner模块的初步学习
文章目录
- 一、前言
- 二、概念
- 2.1 安装
- 2.2 窗口
- 三、小部件
- 3.1 概述
- 3.2 常用小部件
- 3.2.1 Label
- 3.2.2 Button
- 3.2.3 Entry
- 3.2.4 Text
- 3.2.5 Listbox
- 3.2.6 Checkbutton
- 3.2.7 Radiobutton
- 3.2.8 Scrollbar
- 3.3 更多小部件
- 3.3.1 Scale
- 3.3.2 Spinbox
- 3.3.3. Progressbar
- 3.4 主题小部件
- 四、几何管理
- 4.1 Frame
- 4.2 pack
- 4.3 place
- 4.4 grid
- 五、事件处理
- 5.1 概述
- 5.2 bind
- 5.3 其他方法
- 六、复杂窗口
- 6.1 常用窗口
- 6.2 消息框
- 6.3 分隔符
- 6.4 Label Frames
- 6.5 paned windows
- 6.6 Notebook
- 6.7 Treeview
一、前言
https://realpython.com/python-gui-tkinter/
https://tkdocs.com/tutorial/index.html
二、概念
2.1 安装
tinker是python内置的标准GUI框架,可跨平台使用。可使用以下命令确保Tkinter正常工作:
import tkinter as tk
tk._test()
弹出一个窗口,显示Tcl/Tk的版本为8.6,要确保不是8.4或8.5
除此之外,也可以使用其他命令获取确切版本
import tkinter as tk
version = tk.Tcl().eval('info patchlevel')
print('Tk version:', tk.TkVersion, '(Tcl version:', version, ')')
# Tk version: 8.6 (Tcl version: 8.6.12 )
2.2 窗口
窗口是tkinter GUI基础元素,是所有其他GUI元素所在的容器。
import tkinter as tk
window = tk.Tk()
窗口常用的方法:
方法 | 描述 |
---|---|
title() | 设置标题 |
geometry() | 设置窗口大小 |
resizable(FALSE,FLASE) | 调整窗口大小,第一个参数控制用户是否可以修改宽度,第二个则是是否可以更改高度 |
destroy() | 关闭窗口,所有子窗口也会被关闭 |
attributes("-fullscreen",1) | 全屏 |
withdraw() | 隐藏窗口 |
三、小部件
3.1 概述
1️⃣有了窗口后,就可以添加部件,部件是用户与程序交互的元素,部件类型有:
部件 | 描述 |
---|---|
Label | 显示文本 |
Button | 按钮 |
Entry | 仅允许输入一行文本的文本输入框 |
Text | 允许输入多行的文本输入框 |
Frame | 对部件进行分组或在部件之间提供填充的矩形区域 |
命名规范:
小部件可选择的标准参数选项
选项 | 描述 |
---|---|
activebackground | 鼠标悬停时的背景颜色 |
activeforeground | 鼠标悬停时的前景颜色 |
anchor | 文本或图象在标签中的对齐方式,如n、s、e、w等 |
background | 背景颜色 |
foreground | 前景颜色 |
font | 字体样式 |
text | 显示的文本内容 |
relief | 边框样式,有flat 无边框、raised 凸起效果、sunken 凹陷效果、groove 凹槽效果、ridge 立体效果 |
padx | 水平内边距 |
pady | 垂直内边距 |
cursor | 鼠标悬停时的光标样式 |
borderwidth 或bd | 边框宽度 |
state | 组件状态,有normal、disabled 禁用、active |
highlightbackground | 高亮背景颜色 |
highlightcolor | 高亮颜色 |
highlightthickness | 高亮边框厚度 |
takefocus | 是否可以通过键盘聚焦 |
underline | 下划线索引(指定哪个字符带有下划线) |
部件设置属性的常用方式:
# 创建时直接指定
label = tk.Label(root, text="Hello", bg="yellow", font=("Arial", 14))
# 通过config方法动态修改
button.config(bg="red", state="disabled") # 禁用按钮并变红
# 通过绑定变量控制
text_var = tk.StringVar()
entry = tk.Entry(root, textvariable=text_var)
text_var.set("默认文本") # 动态更新输入框内容
2️⃣创建部件后,还需要将部件添加到窗口中,在几何管理器一节详细介绍:
方法 | 描述 |
---|---|
pack() | |
grid() | 不推荐,布局难管理,没有响应式布局 |
place() | 推荐使用 |
3️⃣最后还需执行事件循环来监听事件
window.mainloop()
3.2 常用小部件
3.2.1 Label
可使用Label小部件来添加文本,格式为Label(master=None, cnf={}, **kw)
,
master
为父容器cnf
为配置字典,用于传递配置选项,通常与**kw
参数互斥**kw
为关键字参数,用于传递配置选项,支持标准选项和特定于小部件的选项,推荐使用
文本部件特定选项:
特定选项 | 描述 |
---|---|
height | 高度(以文本行为单位) |
width | 宽度(以字符为单位) |
justify | 多行文本对齐方式,有left、center等 |
wraplength | 文本自动换行的宽度,以像素为单位 |
添加文本部件
greeting = tk.Label(text='Hello, world!')
greeting.pack()
window.mainloop()
为文本部件添加一些样式
label = tk.Label(
root,
text="欢迎使用 Tkinter",
font=("Arial", 16),
foreground="white",
background="blue",
width=20,
height=2,
anchor="center",
relief="solid",
borderwidth=2
)
label.pack(pady=20)
root.mainloop()
3.2.2 Button
小部件Button可用于显示可点击按钮,格式为Button(master=None, cnf={}, **kw)
,
特定选项 | 描述 |
---|---|
command | 绑定到按钮上的回调函数(点击时触发) |
compound | 图文混合时的排列方式,有left、right、top、bottom、center |
default | 指定按钮是否为默认按钮,有normal、active、disabled |
height | 高度(以文本行为单位) |
overrelief | 鼠标悬停时的边框样式 |
image | 显示图片,需要配合PhotoImage使用 |
width | 宽度(以字符为单位) |
尝试使用Button小部件:
import tkinter as tk
def on_button_click():
print("按钮被点击了!")
root = tk.Tk()
button = tk.Button(
root,
text="点击我",
font=("Arial", 14),
foreground="white",
background="blue",
width=10,
height=2,
anchor="center",
relief="raised",
borderwidth=2,
command=on_button_click # 绑定点击事件
)
button.pack(pady=20)
root.mainloop()
3.2.3 Entry
使用Entry小部件获取用户输入,格式为Entry(master=None, cnf={}, **kw)
,
特定选项 | 描述 |
---|---|
exportselection | 是否将选中的文本导出到系统剪贴板 |
insertbackground | 插入光标的颜色 |
insertborderwidth | 插入光标的边框宽度 |
insertwidth | 插入光标的宽度 |
selectbackground | 选中文本的背景颜色 |
selectborderwidth | 选择文本的边框宽度 |
selectforeground | 选中文本的前景颜色 |
show | 显示替代字符,如密码输入时显示* |
validate | 指定何时进行验证,如none、focus、focusin、focusout、key |
validatecommand 或vcmd | 验证输入时调用的回调函数 |
invalidcommand 或invcmd | 验证失败时调用的回调函数 |
textvariable | 与Entry关联的变量 |
关于validatecommand
参数使用:
1️⃣注册验证函数:使用root.register(validation_function)
注册验证函数,返回一个命令对象。验证函数需要返回一个布尔值(True 或 False),表示输入是否有效
2️⃣指定验证触发时机:使用validate参数指定验证触发时机,常见的有:
值 | 描述 |
---|---|
none | 不进行验证 |
focus | 焦点进入或离开时 |
focusin | 焦点进入时 |
focusout | 焦点离开时 |
key | 每次按键 |
all | 上述所有情况 |
3️⃣传递验证参数:常见的有
值 | 描述 |
---|---|
%P | 表示输入框的新值 |
%S | 插入的字符 |
%V | 验证触发的原因 |
%W | 小部件的名称 |
%i | 插入字符的位置 |
%d | 动作类型,1表示插入;0表示删除 |
尝试使用Entry小部件来验证输入:
import tkinter as tk
def validate_input(new_value):
"""验证输入是否为数字"""
if new_value.isdigit() or new_value == "":
return True
return False
root = tk.Tk()
root.title("Entry 示例")
# 创建一个 StringVar 变量,用于绑定 Entry 的内容
entry_var = tk.StringVar()
# 配置 validatecommand
vcmd = (root.register(validate_input), '%P') # %P 表示新值
entry = tk.Entry(
root,
textvariable=entry_var,
font=("Arial", 14),
foreground="black",
background="white",
width=20,
justify="center",
validate="key", # 在按键时验证
validatecommand=vcmd, # 验证命令
show="" # 显示原始输入
)
entry.pack(pady=20)
root.mainloop()
除此之外,Entry还可以对用户输入的数据进行操作:
操作 | 描述 |
---|---|
get() | 检索文本 |
delete(first,last=None) | 删除文本,可以删除单个字符,也可以删除多个字符工作原理与字符串切片类似。 |
insert(index,text) | 插入文本 |
Select_clear() | 清空文本框 |
举个栗子:创建Entry部件,当中输入框里输入值时,控制台会打印输入的文本
import tkinter as tk
def get_entry_value():
name = entry.get()
print("输入的值:", name)
def delete_entry_value():
entry.delete(0, tk.END)
print("已删除所有内容")
def insert_entry_value():
entry.insert(0, "python")
print("已插入 'python'")
window = tk.Tk()
entry = tk.Entry(window, font=("Arial", 14))
entry.pack(pady=20)
button1 = tk.Button(window, text="获取输入", command=get_entry_value)
button1.pack(pady=10)
button2 = tk.Button(window, text="删除输入", command=delete_entry_value)
button2.pack(pady=10)
button3 = tk.Button(window, text="增加输入", command=insert_entry_value)
button3.pack(pady=10)
window.mainloop()
3.2.4 Text
Text小部件用于输入文本,与Entry小部件相似,不同之处在于Text小部件可包含多行文本
特定选项 | 描述 |
---|---|
setgrid | 是否设置网络 |
xscrollcommand | 与水平滚动条关联的命令 |
yscrollcommand | 与垂直滚动条关联的命令 |
autoseparators | 是否自动插入分隔符 |
spacing1 | 段落前的间距 |
spacing2 | 段落间的间距 |
spacing3 | 段落后的间距 |
undo | 是否启用撤销功能,可以通过text.edit_undo() 和text.edit_redo() 进行撤销和重做操作 |
maxundo | 最大撤销操作数 |
wrap | 文本换行方式,有none 不换行、char 按字符换行、word 按单词换行 |
height | 高度(以文本行为单位) |
tabs | 制表符宽度 |
尝试使用Text小部件:
import tkinter as tk
root = tk.Tk()
root.title("Text 示例")
text = tk.Text(
root,
font=("Arial", 14),
foreground="black",
background="white",
width=40,
height=10,
wrap="word", # 按单词换行
undo=True, # 启用撤销功能
insertbackground="blue", # 插入光标颜色
insertwidth=2, # 插入光标宽度
highlightbackground="lightgray",
highlightcolor="gray",
highlightthickness=2,
padx=10,
pady=10,
relief="groove",
state="normal",
xscrollcommand=None,
yscrollcommand=None
)
text.pack(pady=20)
root.mainloop()
同样的,Text与Entry一样可以对用户的输入进行操作
方法 | 描述 |
---|---|
get(位置) | 获取文本(包括换行符),其中获取单个字符"行号.字符位置" ,行号以1开始,字符位置以0开始,多个字符需要起始索引和结束索引。 |
delete(位置) | 删除文本,可以删除单个字符,也可以删除多个字符工作原理与字符串切片类似。 |
insert(位置,文本) | 插入文本,如果要将文本插入到新行,需要在插入的文本中增加换行符 |
import tkinter as tk
def get_text_value():
name = text.get("1.0",tk.END)
print("输入的值:", name)
def delete_text_value():
text.delete("1.0", tk.END)
print("已删除所有内容")
def insert_text_value():
text.insert("1.0", "python")
print("已插入 'python'")
root = tk.Tk()
text = tk.Text(root, font=("Arial", 14))
text.pack(pady=20)
button1 = tk.Button(root, text="获取输入", command=get_text_value)
button1.pack(pady=10)
button2 = tk.Button(root, text="删除输入", command=delete_text_value)
button2.pack(pady=10)
button3 = tk.Button(root, text="增加输入", command=insert_text_value)
button3.pack(pady=10)
root.mainloop()
3.2.5 Listbox
Listbox列表框允许用户选择一个或多个,格式Entry(master=None, cnf={}, **kw)
,
特定选项 | 描述 |
---|---|
listvariable | 与Listbox关联的变量,通常使用StringVar或Variable |
selectmode | 选择模式,有single 单选、browse 单选但可以通过Shift键选择范围、multiple 多选、extended 单选但可以通过Shift键选择范围或者通过Ctrl键选择多个 |
activestyle | 选中项的高亮模式,有dotbox、none |
除此之外,还有以下常用方法:
方法 | 描述 |
---|---|
insert(index,*elements) | 在指定位置插入新项目 |
get(first,last=None) | 获取指定范围内的项目内容 |
delete(first,last=None) | 删除指定范围内的项目 |
curselection() | 返回当前选中的项目索引列表 |
使用实例
import tkinter as tk
root = tk.Tk()
# 创建 Listbox
listbox = tk.Listbox(root)
listbox.pack(fill="both", expand=True)
# 插入项目
for i in range(10):
listbox.insert(tk.END, f"项目 {i+1}")
# 设置选中状态
listbox.selection_set(2, 4) # 选中索引 2 到 4 的项目
# 滚动到指定项目
listbox.see(8) # 确保索引为 8 的项目可见
# 获取选中的项目
selected_indices = listbox.curselection()
print("选中的项目索引:", selected_indices)
# 获取项目总数
count = listbox.size()
print("项目总数:", count)
# 修改项目样式
listbox.itemconfigure(1, background="green", foreground="white")
root.mainloop()
3.2.6 Checkbutton
Checkbutton用于勾选选项,格式为Checkbutton(master=None, cnf={}, **kw)
,
特定选项 | 描述 |
---|---|
command | 绑定回调函数 |
onvalue | 选中时的值,默认为1 |
offvalue | 未选中时的值,默认为0 |
variable | 与Checkbutton关联的变量,通常使用IntVar或BooleanVar |
indicatoron | 是否显示复选框的小方块 |
selectcolor | 小方块内勾选的颜色 |
import tkinter as tk
def on_checkbutton_change():
print("Checkbutton 状态:", var.get())
root = tk.Tk()
root.title("Checkbutton 示例")
# 创建一个 IntVar 变量,用于绑定 Checkbutton 的状态
var = tk.IntVar()
checkbutton = tk.Checkbutton(
root,
text="选择我",
variable=var,
command=on_checkbutton_change,
font=("Arial", 14),
foreground="black",
background="white",
width=20,
height=2,
anchor="w",
relief="groove",
borderwidth=2,
state="normal",
offvalue=0,
onvalue=1,
selectcolor="lightblue",
underline=0
)
checkbutton.pack(pady=20)
root.mainloop()
3.2.7 Radiobutton
Radiobutton用于创建一组互斥的选项按钮,用户只能从中选择一个
特定选项 | 描述 |
---|---|
command | 绑定回调函数 |
value | 单选按钮的值 |
variable | 与Radiobutton关联的变量,通常使用IntVar或BooleanVar |
import tkinter as tk
def on_radiobutton_change():
print("选中的值:", var.get())
root = tk.Tk()
root.title("Radiobutton 示例")
# 创建一个 IntVar 变量,用于绑定 Radiobutton 的状态
var = tk.IntVar()
# 创建 Radiobutton 小部件
radiobutton1 = tk.Radiobutton(
root,
text="选项 1",
variable=var,
value=1,
command=on_radiobutton_change,
font=("Arial", 14),
foreground="black",
background="white",
width=20,
height=2,
anchor="w",
relief="groove",
borderwidth=2,
state="normal",
selectcolor="lightblue",
underline=0
)
radiobutton1.pack(pady=10)
radiobutton2 = tk.Radiobutton(
root,
text="选项 2",
variable=var,
value=2,
command=on_radiobutton_change,
font=("Arial", 14),
foreground="black",
background="white",
width=20,
height=2,
anchor="w",
relief="groove",
borderwidth=2,
state="normal",
selectcolor="lightblue",
underline=0
)
radiobutton2.pack(pady=10)
root.mainloop()
3.2.8 Scrollbar
Scrollbar滚动条可以帮助用户查看另一个部件的剩余部分
特定选项 | 描述 |
---|---|
orient | 滚动条的方向,tk.VERTICAL 表示垂直滚动条;tk.HORIZONTAL 表示水平滚动条 |
jump | 是否允许拖动滑块直接跳转到某个位置 |
troughcolor | 滑槽的颜色 |
width | 滚动条的宽度(以像素为单位) |
command | 绑定回调函数,接受两个参数first、last分别表示滚动条可见区域的起始位置和结束位置,浮点数表示,范围为0到1 |
config
方法可以用于动态修改滚动条配置选项,格式为config(cnf=None,**kw)
cnf
:如果为字典类型,表示要批量设置的选项,如果是字符串类型,则表示要查询的选项名称**kw
:键值对形式,用于动态修改滚动条的配置
绑定滚动条与小部件的command参数:
- text.yview:将垂直滚动条与Text小部件的垂直滚动关联
- text.xview:将水平滚动条与Text小部件的水平滚动关联
import tkinter as tk
root = tk.Tk()
root.title("Scrollbar使用")
# 创建两个滚动条
scrollbar_v = tk.Scrollbar(root)
scrollbar_v.pack(side=tk.RIGHT, fill=tk.Y)
scrollbar_h = tk.Scrollbar(root, orient=tk.HORIZONTAL)
scrollbar_h.pack(side=tk.BOTTOM, fill=tk.X)
# 创建一个文本框
text = tk.Text(root, height=10, width=30, yscrollcommand=scrollbar_v.set)
text.pack(side=tk.LEFT, fill=tk.BOTH)
scrollbar_v.config(command=text.yview)
scrollbar_h.config(command=text.xview)
root.mainloop()
3.3 更多小部件
3.3.1 Scale
Scale组件允许用户直接选择数值
特定选项 | 描述 |
---|---|
tickinterval | 刻度线的间隔 |
resolution | 刻度的最小变化值,默认为1 |
digits | 显示值的小数位数 |
from_ | 刻度的起始值 |
to | 刻度的结束值 |
variable | 与Scale关联的变量,通常使用IntVar或DoubleVar |
command | 回调函数,会接受当前值作为参数 |
showvalue | 是否显示当前值 |
orient | 刻度的方向,有horizontal和vertical |
import tkinter as tk
# 创建主窗口
root = tk.Tk()
root.title("Scale 示例")
# 定义回调函数
def on_scale_change(value):
print(f"当前值: {value}")
# 创建水平刻度条
scale_horizontal = tk.Scale(
root,
from_=0,
to=100,
orient="horizontal",
length=300,
tickinterval=10,
command=on_scale_change
)
scale_horizontal.pack(pady=20)
# 创建垂直刻度条
scale_vertical = tk.Scale(
root,
from_=0,
to=100,
orient="vertical",
length=200,
tickinterval=10,
command=on_scale_change
)
scale_vertical.pack(padx=20, side="right")
# 运行主循环
root.mainloop()
3.3.2 Spinbox
Spinbox组件允许用户在给定范围内选择数字
特定参数 | 描述 |
---|---|
buttondownrelief | 向下按钮的边框样式 |
buttonuprelief | 向上按钮的边框样式 |
command | 绑定回调函数 |
format | 显示值的格式 |
from_ | 刻度的起始值 |
to | 刻度的结束值 |
increment | 每次点击按钮值的变化量 |
readonlybackground | 只读状态下的背景颜色 |
validate | 指定何时进行验证 |
validatecommand | 验证输入时调用的回调函数 |
values | 可选值列表,优先级高于from_ 和to |
width | 宽度 |
wrap | 是否允许值在达到最大值后绕回到最小值 |
textvariable | 与文本关联的变量 |
import tkinter as tk
def on_spinbox_change(*args):
print("当前值:", spinbox.get())
root = tk.Tk()
root.title("Spinbox 示例")
# 创建一个 IntVar 变量,用于绑定 Spinbox 的值
spinbox_var = tk.IntVar()
# 创建 Spinbox 小部件
spinbox = tk.Spinbox(
root,
from_=0,
to=100,
increment=5,
textvariable=spinbox_var,
command=on_spinbox_change,
font=("Arial", 14),
width=10,
justify="center",
wrap=True,
state="normal",
buttonbackground="lightgray",
buttoncursor="arrow",
buttondownrelief="sunken",
buttonuprelief="raised",
relief="groove",
takefocus=True,
validate="key",
validatecommand=(root.register(lambda value: value.isdigit() or value == ""), "%P")
)
spinbox.pack(pady=20)
root.mainloop()
3.3.3. Progressbar
Progressbar部件用于显示进度状态,有两种模式:确定模式和不确定模式,格式:Progressbar(master=None, **kw)
参数 | 描述 |
---|---|
orient | 进度条方向,默认horizontal水平方向,vertical垂直方向 |
mode | 工作模式,有determinate确定模式,显示已完成的任务量;indeterminate不确定模式,显示动画效果,表示任务正在进行中。 |
maximum | 进度条最大值,默认为100 |
value | 设置当前进度值,仅在确定模式下有效 |
variable | 绑定一个变量 |
phase | 不常用,表示不确定模式下的动画阶段 |
方法介绍:
方法 | 描述 |
---|---|
start(interval=None) | 开始自动递增模式,每隔interval毫秒就调用step方法 |
step(amount=None) | 手动递增进度条的值 |
stop() | 停止自动递增模式 |
import tkinter as tk
from tkinter import ttk
# 创建主窗口
root = tk.Tk()
root.title("Progressbar 示例")
# 创建进度条
progress = ttk.Progressbar(root, orient="horizontal", length=300, mode="determinate")
progress.pack(pady=20)
# 初始化进度值
progress['value'] = 0
# 定义按钮点击事件
def start_progress():
if progress['value'] < 100:
progress['value'] += 10 # 每次增加 10
root.after(500, start_progress) # 每隔 500 毫秒调用一次
# 创建按钮
button = ttk.Button(root, text="开始", command=start_progress)
button.pack()
# 运行主循环
root.mainloop()
3.4 主题小部件
Tkinter目前有两大类小部件:优先使用ttk部件
- 经典小部件:tkinter,简单易用,例如
tkinter.Label
- 主题小部件:ttk,默认使用操作系统的原生外观,可看作Tkinter的CSS,例如
tkinter.ttk.Label
在使用Tkinter的主题部件时,通常使用以下命令
import tkinter as tk
import tkinter.ttk as ttk
尝试使用部件:
tk.Label()
ttk.Label()
也可以使用更便捷的用法,主题小部件会自动覆盖旧部件:
from tkinter import *
from tkinter.ttk import *
Label()
Text()
四、几何管理
同一窗口中只能使用一种布局管理器 |
4.1 Frame
空的Frame小部件实际上是不可见的,一般用来做其他小部件的容器master参数值
frame = tk.Frame()
label = tk.Label(master=frame)
Frame适合以逻辑方式布置其他小部件
import tkinter as tk
window = tk.Tk()
frame_a = tk.Frame()
frame_b = tk.Frame()
label_a = tk.Label(master=frame_a, text="I'm in Frame A")
label_a.pack()
label_b = tk.Label(master=frame_b, text="I'm in Frame B")
label_b.pack()
frame_a.pack()
frame_b.pack()
window.mainloop()
如果将frame_a.pack()
与frame_b.pack()
的顺序调换:
4.2 pack
pack使用打包算法将小部件按指定顺序放在Frame或窗口中,是响应式布局;默认情况下将每个组件放置在前一个组件的下方
import tkinter as tk
window = tk.Tk()
frame1 = tk.Frame(master=window, width=100, height=100, bg="red")
frame1.pack()
frame2 = tk.Frame(master=window, width=50, height=50, bg="yellow")
frame2.pack()
frame3 = tk.Frame(master=window, width=25, height=25, bg="blue")
frame3.pack()
window.mainloop()
pack的一些常用参数
常用参数 | 描述 |
---|---|
fill | 指定哪个方向填充,tk.X 为填充水平方向;tk.Y 为填充垂直方向;使用tk.BOTH 同时需要设置expand |
side | 指定放置在窗口哪一侧,tk.TOP (默认)、tk.BOTTOM 、tk.LEFT 、tk.RIGHT |
使用fill
参数后填充水平方向:frame1.pack(fill=tk.X)
使用side
参数后填充水平方向:frame1.pack(side=tk.LEFT, fill=tk.Y)
4.3 place
place可以控制精确位置,需要x和y参数来指定小部件左上角的x和y,以像素为单位。place很难管理布局,且没有响应式,一般不推荐使用。
import tkinter as tk
window = tk.Tk()
frame = tk.Frame(master=window, width=150, height=150)
frame.pack()
label1 = tk.Label(master=frame, text="I'm at (0, 0)", bg="red")
label1.place(x=0, y=0)
label2 = tk.Label(master=frame, text="I'm at (75, 75)", bg="yellow")
label2.place(x=75, y=75)
window.mainloop()
4.4 grid
grid通过将窗口或Frame拆分为行和列来工作
常用参数 | 描述 |
---|---|
row | 行索引 |
column | 列索引 |
padx | 水平方向上添加外部填充 |
pady | 垂直方向上添加外部填充 |
sticky | 更改网格内标签的位置,有n、w、e、s |
import tkinter as tk
window = tk.Tk()
for i in range(3):
for j in range(3):
frame = tk.Frame(
master=window,
relief=tk.RAISED,
borderwidth=1
)
frame.grid(row=i, column=j)
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
label.pack()
window.mainloop()
除此之外可以使用columnconfigure
方法和rowconfigure
方法来实现网格适应窗口变化,它们都有共同的基本参数:
- index:索引,要配置的网格列或者行索引,如果要配置多个则采用列表形式
- Weight:确定列或行要如何响应窗口(相对于其他行或列),为0时这列/行不会随窗口大小而扩展,如果都为1时以相同速率生长
- Minimum Size:设置行高度或列宽度的最小尺寸,以像素为单位
import tkinter as tk
window = tk.Tk()
for i in range(3):
window.columnconfigure(i, weight=1, minsize=75)
window.rowconfigure(i, weight=1, minsize=50)
for j in range(0, 3):
frame = tk.Frame(
master=window,
relief=tk.RAISED,
borderwidth=1
)
frame.grid(row=i, column=j, padx=5, pady=5)
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
label.pack(padx=5, pady=5)
window.mainloop()
grid与pack在填充上对应关系:推荐使用grid
五、事件处理
5.1 概述
使用window.mainloop()
来启动事件循环,使用bind
或者command
方法来绑定事件,command
方法一般不接受参数。
事件发生时,会从触发该事件的小部件开始,逐级向上传播到其父容器直到根窗口,这个过程称为事件传播,如果希望阻止事件传播到父容器,可以在回调函数中返回break
。
Event事件对象常用属性有:
属性 | 描述 |
---|---|
x ,y | 事件发生的坐标(相对于组件) |
x_root ,y_root | 事件发生的坐标(相对于屏幕) |
keysym | 按键的符号名称 |
widget | 触发事件的小部件 |
type | 事件类型 |
time | 事件发生的时间戳 |
事件模式由<MODIFIER-MODIFIER-TYPE-DETAIL>
构成
1️⃣MODIFIER
修饰符用于指定按键组合,常见的有:
修饰符 | 描述 |
---|---|
Control | Ctrl键 |
Alt | Alt键 |
Meta | Meta键,通常对应Windows键或Command键 |
Shift | Shift键 |
Lock | Caps Lock键 |
Button1 到Button5 | 鼠标按钮 |
2️⃣Type
事件类型定义了具体的事件类型,常见的有:
事件 | 描述 |
---|---|
Activate | 窗口激活,通常指state 的变化 |
Deactivate | 从活动状态变为不活动状态,通常指state 的变化 |
Button | 左键点击、左键双击 |
ButtonRelease | 释放鼠标按钮 |
Destroy | 小部件销毁 |
Enter | 鼠标进入小部件区域 |
Leave | 鼠标移出部件区域 |
Motion | 鼠标拖动 |
FocusIn | 进入焦点 |
FocusOut | 退出焦点 |
MouseWheel | 滚轮滚动 |
Configure | 窗口大小或位置改变 |
KeyPress 或Key | 键盘按键按下 |
KeyRelease | 键盘按键释放 |
3️⃣DETAIL
细节时对事件类型的详细补充,例如对于Button来说,1表示左键,3表示右键;而对于KeyPress来说,细节是按键的符号名称。
5.2 bind
使用bind方法来绑定事件,格式为bind(sequence=None, func=None, add=None)
- sequence:事件序列,由一个或多个事件模式组成,格式为
<MODIFIER-MODIFIER-TYPE-DETAIL>
,例如<Control-c>
按下
Ctrl + C;<<CustomEvent>>
自定义虚拟事件。- func:事件发生时执行的函数,接受Event实例,如果返回值为break则阻止后续函数调用。
- add:为True时则将新绑定函数添加到现有绑定中,如果为False则替换现有绑定。
如果绑定了事件,可以用unbind
来删除绑定:widget.unbind("<事件类型>")
绑定鼠标事件:
import tkinter as tk
# 创建主窗口
root = tk.Tk()
# 定义回调函数
def on_click(event):
print(f"鼠标点击位置: ({event.x}, {event.y})")
# 创建按钮
button = tk.Button(root, text="点击我")
button.pack(pady=20)
# 绑定鼠标左键点击事件
button.bind("<Button-1>", on_click)
# 运行主循环
root.mainloop()
虚拟事件是用户自定义的事件,通过event_generate
来触发
import tkinter as tk
# 创建主窗口
root = tk.Tk()
# 定义回调函数
def on_custom_event(event):
print("触发了自定义事件")
# 创建按钮
button = tk.Button(root, text="触发事件")
button.pack(pady=20)
# 绑定虚拟事件
button.bind("<<CustomEvent>>", on_custom_event)
# 触发虚拟事件
button.event_generate("<<CustomEvent>>")
# 运行主循环
root.mainloop()
5.3 其他方法
bind_class(class_name,sequence,func)
:为特定类名的小部件绑定事件
root.bind_class("Button", "<Button-1>", lambda event: print("任意按钮点击"))
bind_all(sequence,func)
:为所有小部件绑定事件
root.bind_all("<KeyPress>", lambda event: print(f"按键: {event.keysym}"))
六、复杂窗口
6.1 常用窗口
filedialog模块可以对文件或目录进行操作
from tkinter import filedialog
# 打开文件对话框
open_filename = filedialog.askopenfilename(title="选择要打开的文件")
if open_filename:
print(f"选择的文件路径: {open_filename}")
# 保存文件对话框
save_filename = filedialog.asksaveasfilename(title="选择保存文件的位置")
if save_filename:
print(f"保存的文件路径: {save_filename}")
# 选择文件夹对话框
dirname = filedialog.askdirectory(title="选择一个文件夹")
if dirname:
print(f"选择的文件夹路径: {dirname}")
进行颜色选择:
from tkinter import colorchooser
# 创建颜色选择器对话框
color_chooser = colorchooser.Chooser(
initialcolor="#ff0000", # 初始颜色为红色
title="选择颜色" # 对话框标题
)
rgb, hex_color = color_chooser.show()
if rgb and hex_color:
print(f"RGB: {rgb}, Hex: {hex_color}")
else:
print("用户取消了颜色选择")
选择字体:
import tkinter as tk
from tkinter import ttk
def font_changed(font):
if font: # 如果用户没有取消选择
print("Selected font:", font)
label.config(font=font)
def font_choice() -> None:
root.tk.call('tk', 'fontchooser', 'configure',
'-font', current_font.get(),
'-command', root.register(font_changed))
root.tk.call('tk', 'fontchooser', 'show')
root = tk.Tk()
root.title("字体选择示例")
root.geometry("300x150")
current_font = tk.StringVar(value="helvetica 24")
label = ttk.Label(root,text="你好啊",font=current_font.get(),padding=(10, 10))
label.pack(pady=10)
btn = ttk.Button(root,text="选择字体",command=font_choice)
btn.pack(pady=10)
root.mainloop()
6.2 消息框
使用messagebox模块来应用消息提示框,有不同类型的提示框
类型 | 描述 |
---|---|
showinfo(title,message,**options) | 信息对话框 |
shhowwaring(title,message,**options) | 警告对话框 |
showerror(title,message,**options) | 错误对话框 |
askquestion(title,message,**options) | 带有是和否的问题对话框 |
askokcancel(title,message,**options) | 带有确定和取消的对话框 |
askyesno(title,message,**options) | 带有是和否的对话框 |
askyesnocancel(title,message,**options) | 带有是、否、取消的对话框 |
askretrycancel(title,message,**options) | 带有重试和取消的对话框 |
参数解析:
参数 | 描述 |
---|---|
title | 对话框的标题 |
message | 对话框显示的消息文本 |
icon | 显示的图标类型,有ERROR,INFO,QUESTION,WARNING |
default | 设置默认按钮 |
parent | 指定对话框的父窗口 |
使用示例:
from tkinter import messagebox
messagebox.showinfo(message='Have a good day')
messagebox.askyesno(message='Are you sure you want to install SuperVirus?',
icon='question', title='Install')
6.3 分隔符
使用Separator部件在小部件之间放置水平或垂直线来实现分隔
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("Separator 示例")
root.geometry("300x200")
separator_horizontal_grid = ttk.Separator(root, orient=tk.HORIZONTAL)
separator_horizontal_grid.grid(row=1, column=0, sticky="ew", padx=10, pady=10)
separator_vertical_grid = ttk.Separator(root, orient=tk.VERTICAL)
separator_vertical_grid.grid(row=0, column=1, rowspan=3, sticky="ns", padx=10, pady=10)
# 添加一些标签以显示分隔线的效果
label1 = ttk.Label(root, text="标签 1")
label1.grid(row=0, column=0, padx=10, pady=10)
label2 = ttk.Label(root, text="标签 2")
label2.grid(row=2, column=0, padx=10, pady=10)
label3 = ttk.Label(root, text="标签 3")
label3.grid(row=0, column=2, padx=10, pady=10)
root.mainloop()
6.4 Label Frames
label Frames称为标签帧,一些常用参数:
参数 | 描述 |
---|---|
labelanchor | 指定标签的位置,如n 顶部中间,nw 左上角 |
padding | 设置内部部件与边框之间的间距 |
labelwidget | 使用自定义的小部件作为标签 |
class_ | 小部件的类名,通常不需要手动设置 |
style | 小部件的样式,需要提前通过ttk.Style定义 |
takefocus | 指定小部件是否可以通过Tab键获取焦点 |
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("Labelframe 示例")
# 创建一个 Labelframe
labelframe = ttk.Labelframe(root, text="这是一个 Labelframe", labelanchor="n")
labelframe.pack(padx=10, pady=10, fill="both", expand=True)
# 在 Labelframe 内添加其他小部件
label = ttk.Label(labelframe, text="这是 Labelframe 内部的内容")
label.pack(padx=5, pady=5)
root.mainloop()
6.5 paned windows
常用参数:
参数 | 描述 |
---|---|
class_ | 小部件的类名,通常不需要手动设置 |
orient | 设置子窗口的排列方式,值为horizontal 或vertical |
takefocus | 指定小部件是否可以通过Tab键获取焦点 |
常用方法:
方法 | 描述 |
---|---|
add(child,**kw) | 添加一个子窗口,**kw 为子窗口选项(如weight权重) |
insert(pos,child,**kw) | 在指定位置插入一个子窗口 |
forget(child) | 移除指定子窗口 |
sashpos(index,newpos=None) | 获取或设置分割条的位置,newpos为新的位置 |
pane(pane,option=None,**kw) | 查询或修改子窗口的选项,pane为子窗口索引或名称,option如果指定则返回该选项的值 |
使用实例
import tkinter as tk
from tkinter import ttk
# 创建主窗口
root = tk.Tk()
root.title("Panedwindow 示例")
# 创建一个水平方向的 Panedwindow
paned_window = ttk.Panedwindow(root, orient="horizontal")
paned_window.pack(fill="both", expand=True)
# 添加第一个子窗口
frame1 = ttk.Frame(paned_window, width=100, height=200, relief="sunken")
paned_window.add(frame1)
# 添加第二个子窗口
frame2 = ttk.Frame(paned_window, width=300, height=200, relief="sunken")
paned_window.insert(0, frame2)
# 查询子窗口选项
options = paned_window.pane(0)
print(options) # {'weight': 0}
# 运行主循环
root.mainloop()
6.6 Notebook
Notebook用于管理一组窗口,同一时间只显示一个窗口,每个子窗口与一个标签关联。
参数 | 描述 |
---|---|
class | 指定窗口类名,通常不设置 |
style | 样式 |
padding | 标签之间的间距 |
常用方法如下所示,其中tab_id
可以是标签索引(从0开始)、子窗口名称、位置规格(如@x,y
表示鼠标指针所在位置的标签)、current(当前选中的标签)等
方法 | 描述 |
---|---|
add(child,**kw) | 添加新的标签页 |
forget(tab_id) | 移除指定的标签页 |
hide(tab_id) | 隐藏指定的标签页 |
select(tab_id=None) | 选择指定的标签页 |
insert(pos,child,**kw) | 指定位置插入一个标签页 |
enable_traversal() | 启用键盘导航功能,有Ctrl+Tab切换到下一个标签页等 |
tab(tab_id,option=None,**kw) | 查询或修改指定标签的选项 |
使用示例:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("Notebook 示例")
# 创建 Notebook
notebook = ttk.Notebook(root)
notebook.pack(fill="both", expand=True)
# 创建标签页
frame1 = ttk.Frame(notebook, width=200, height=200)
frame2 = ttk.Frame(notebook, width=200, height=200)
# 添加标签页
notebook.add(frame1, text="标签页 1")
notebook.add(frame2, text="标签页 2")
# 在标签页中添加内容
label1 = ttk.Label(frame1, text="这是标签页 1 的内容")
label1.pack(pady=20)
label2 = ttk.Label(frame2, text="这是标签页 2 的内容")
label2.pack(pady=20)
# 启用键盘导航
notebook.enable_traversal()
root.mainloop()
6.7 Treeview
Treeview用于显示分层的项目集合,构造时有以下常用参数:
参数 | 描述 |
---|---|
xscrollcommand | 绑定水平滚动条 |
yscrollcommand | 绑定垂直滚动条 |
columns | 定义除树形结构外的其他列 |
displaycolumns | 指定要显示的列(默认所有列) |
selectmode | 设置选择模式,有browse 单选模式(默认)、extended 多选、none 禁用选择 |
padding | 设置子项之间的间距 |
show | 指定显示的内容,有tree 仅显示树形结构、headings 仅显示列标题、tree headings 同时显示树形结构和列标题 |
除此之外,常用方法有:
方法 | 描述 |
---|---|
delete(*items) | 删除指定项目极其后代 |
exists(item) | 检查指定项目是否存在 |
insert(parent,index,iid=None,**kw) | 插入新项目,parent 参数里"" 表示根项目,iid 表示项目标识符,**kw 项目选项 |
tag_bind(tagname,sequence=None,callback=None) | 为指定标签绑定事件,tagname标签名称;sequence事件序列;callback回调函数 |
项目选项:在插入项目或者修改项目时,通常有以下选项
参数 | 描述 |
---|---|
text | 文本标签 |
image | 图象 |
values | 数据值列表,对应各列 |
open | 指定项目是否展开,可选值为True或False |
tags | 给项目添加标签,用于绑定事件或样式 |
使用案例:
import tkinter as tk
from tkinter import ttk
# 创建主窗口
root = tk.Tk()
root.title("Treeview 示例")
# 创建 Treeview
tree = ttk.Treeview(root, columns=("姓名", "年龄"), show="headings")
tree.pack(fill="both", expand=True)
# 设置列标题
tree.heading("#1", text="姓名")
tree.heading("#2", text="年龄")
# 插入数据
data = [("张三", "25"), ("李四", "30"), ("王五", "22")]
for item in data:
tree.insert("", "end", values=item)
# 添加滚动条
scrollbar = ttk.Scrollbar(root, orient="vertical", command=tree.yview)
scrollbar.pack(side="right", fill="y")
tree.configure(yscrollcommand=scrollbar.set)
# 运行主循环
root.mainloop()