Tkinter GUI 编程指南
返回首页
技术分享
2025-12-20
基础入门
导入库
import tkinter as tk
# 或者
from tkinter import *
创建基本窗口
import tkinter as tk
# 创建主窗口
root = tk.Tk()
root.title("我的第一个GUI程序")
root.geometry("400x300") # 宽度x高度
# 开启主循环
root.mainloop()
窗口属性设置
基本属性
import tkinter as tk
root = tk.Tk()
# 窗口标题
root.title("窗口标题")
# 窗口大小和位置
root.geometry("800x600+100+100") # 宽x高+左边距+上边距
# 最小/最大尺寸
root.minsize(300, 200)
root.maxsize(1200, 800)
# 窗口是否可缩放
root.resizable(True, True) # (宽可缩放, 高可缩放)
root.resizable(False, False) # 固定大小
# 窗口图标
root.iconbitmap("icon.ico") # Windows
# root.iconphoto(True, tk.PhotoImage(file="icon.png")) # 跨平台
# 背景颜色
root.configure(bg="lightblue")
# 窗口透明度 (0.0-1.0)
root.attributes("-alpha", 0.9)
# 窗口置顶
root.attributes("-topmost", True)
# 窗口关闭前执行
def on_closing():
if messagebox.askokcancel("退出", "确定要退出吗?"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
常用组件
Label 标签组件
# 基本标签
label1 = tk.Label(root, text="Hello, Tkinter!")
label1.pack()
# 带样式的标签
label2 = tk.Label(
root,
text="样式化标签",
font=("Arial", 16, "bold"),
fg="white", # 前景色(文字颜色)
bg="blue", # 背景色
padx=10, # 水平内边距
pady=5, # 垂直内边距
relief=tk.RAISED, # 边框样式
bd=2 # 边框宽度
)
label2.pack(pady=10)
# 图片标签
photo = tk.PhotoImage(file="image.png")
image_label = tk.Label(root, image=photo)
image_label.pack()
Entry 输入框组件
# 基本输入框
entry1 = tk.Entry(root)
entry1.pack(pady=5)
# 带样式的输入框
entry2 = tk.Entry(
root,
font=("Arial", 12),
width=30,
bg="lightyellow",
fg="black",
show="*", # 显示字符(密码框用*)
state="normal" # normal, readonly, disabled
)
entry2.pack(pady=5)
# 字符串变量绑定
var_text = tk.StringVar(value="默认文本")
entry3 = tk.Entry(root, textvariable=var_text)
entry3.pack()
# 获取输入内容
def get_input():
content = entry1.get()
print(f"输入内容: {content}")
# 设置输入内容
def set_input():
entry1.delete(0, tk.END) # 清空
entry1.insert(0, "新内容") # 插入
Button 按钮组件
# 基本按钮
button1 = tk.Button(root, text="点击我")
button1.pack(pady=5)
# 带命令的按钮
def on_click():
print("按钮被点击了!")
button2 = tk.Button(
root,
text="提交",
command=on_click,
font=("Arial", 12),
bg="green",
fg="white",
width=10,
height=2,
relief=tk.RAISED,
bd=3
)
button2.pack(pady=5)
# 带参数的按钮命令
def show_message(message):
messagebox.showinfo("提示", message)
button3 = tk.Button(
root,
text="显示消息",
command=lambda: show_message("Hello World!")
)
button3.pack(pady=5)
Text 文本框组件
# 创建文本框
text_widget = tk.Text(root, height=10, width=50)
text_widget.pack(pady=10)
# 插入文本
text_widget.insert(tk.END, "这是文本框内容\n")
text_widget.insert(tk.INSERT, "在光标位置插入")
# 获取所有文本
all_text = text_widget.get(1.0, tk.END)
# 获取选中文本
selected_text = text_widget.get(tk.SEL_FIRST, tk.SEL_LAST)
# 清空文本框
text_widget.delete(1.0, tk.END)
# 添加滚动条
scrollbar = tk.Scrollbar(root)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
text_widget.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=text_widget.yview)
布局管理
pack 布局
# 基本用法
widget.pack()
# 带参数的pack
widget.pack(
side=tk.TOP, # TOP, BOTTOM, LEFT, RIGHT
fill=tk.BOTH, # NONE, X, Y, BOTH
expand=True, # 是否扩展
padx=10, # 水平外边距
pady=5, # 垂直外边距
ipadx=5, # 水平内边距
ipady=2 # 垂直内边距
)
# 示例
label1 = tk.Label(root, text="顶部", bg="red")
label1.pack(side=tk.TOP, fill=tk.X)
label2 = tk.Label(root, text="左侧", bg="green")
label2.pack(side=tk.LEFT, fill=tk.Y)
label3 = tk.Label(root, text="中间", bg="blue")
label3.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
grid 布局
# 基本网格布局
label1 = tk.Label(root, text="用户名:")
label1.grid(row=0, column=0, padx=5, pady=5)
entry1 = tk.Entry(root)
entry1.grid(row=0, column=1, padx=5, pady=5)
label2 = tk.Label(root, text="密码:")
label2.grid(row=1, column=0, padx=5, pady=5)
entry2 = tk.Entry(root, show="*")
entry2.grid(row=1, column=1, padx=5, pady=5)
# 跨行跨列
button = tk.Button(root, text="登录")
button.grid(row=2, column=0, columnspan=2, pady=10)
# 粘性选项
widget.grid(
row=0,
column=0,
sticky="nsew", # 北南东西(上下左右)
padx=5,
pady=5,
ipadx=2,
ipady=2
)
# 配置行列权重
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(1, weight=1)
place 布局
# 绝对定位
label = tk.Label(root, text="绝对定位")
label.place(x=50, y=50)
# 相对定位
button = tk.Button(root, text="相对定位")
button.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
# 尺寸控制
widget.place(
x=100, y=100,
width=200, height=50,
relx=0.1, rely=0.1,
relwidth=0.8, relheight=0.2
)
高级组件
Listbox 列表框
# 创建列表框
listbox = tk.Listbox(root, height=6)
listbox.pack(pady=10)
# 添加项目
items = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]
for item in items:
listbox.insert(tk.END, item)
# 获取选中项
def get_selection():
selection = listbox.curselection()
if selection:
index = selection[0]
item = listbox.get(index)
print(f"选中: {item}")
# 删除选中项
def delete_selection():
selection = listbox.curselection()
if selection:
listbox.delete(selection[0])
# 绑定事件
listbox.bind('<<ListboxSelect>>', lambda e: get_selection())
Combobox 下拉框
from tkinter import ttk
# 创建下拉框
combo = ttk.Combobox(root, values=["选项1", "选项2", "选项3"])
combo.pack(pady=10)
# 设置默认值
combo.set("选项1")
# 获取选中值
def get_combo_value():
selected = combo.get()
print(f"选中: {selected}")
# 只读模式
combo['state'] = 'readonly' # normal, readonly, disabled
Scale 滑块组件
# 创建滑块
scale = tk.Scale(
root,
from_=0,
to=100,
orient=tk.HORIZONTAL, # HORIZONTAL, VERTICAL
length=200,
label="音量",
command=lambda value: print(f"音量: {value}")
)
scale.pack(pady=10)
# 获取滑块值
def get_scale_value():
value = scale.get()
print(f"当前值: {value}")
Checkbutton 复选框
# 变量绑定
var1 = tk.IntVar()
var2 = tk.IntVar()
check1 = tk.Checkbutton(
root,
text="选项1",
variable=var1,
command=lambda: print(f"选项1: {var1.get()}")
)
check1.pack()
check2 = tk.Checkbutton(
root,
text="选项2",
variable=var2
)
check2.pack()
# 检查状态
def check_states():
print(f"选项1: {bool(var1.get())}")
print(f"选项2: {bool(var2.get())}")
Radiobutton 单选框
# 变量绑定
var_radio = tk.StringVar(value="选项1")
radio1 = tk.Radiobutton(
root,
text="选项1",
variable=var_radio,
value="选项1",
command=lambda: print(f"选中: {var_radio.get()}")
)
radio1.pack()
radio2 = tk.Radiobutton(
root,
text="选项2",
variable=var_radio,
value="选项2"
)
radio2.pack()
radio3 = tk.Radiobutton(
root,
text="选项3",
variable=var_radio,
value="选项3"
)
radio3.pack()
菜单栏
创建菜单
# 创建菜单栏
menubar = tk.Menu(root)
root.config(menu=menubar)
# 文件菜单
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="新建", command=lambda: print("新建文件"))
file_menu.add_command(label="打开", command=lambda: print("打开文件"))
file_menu.add_separator()
file_menu.add_command(label="退出", command=root.quit)
# 编辑菜单
edit_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="编辑", menu=edit_menu)
edit_menu.add_command(label="复制", command=lambda: print("复制"))
edit_menu.add_command(label="粘贴", command=lambda: print("粘贴"))
# 帮助菜单
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="帮助", menu=help_menu)
help_menu.add_command(label="关于", command=lambda: messagebox.showinfo("关于", "Tkinter程序"))
右键菜单
def show_context_menu(event):
context_menu.post(event.x_root, event.y_root)
# 创建右键菜单
context_menu = tk.Menu(root, tearoff=0)
context_menu.add_command(label="剪切", command=lambda: print("剪切"))
context_menu.add_command(label="复制", command=lambda: print("复制"))
context_menu.add_command(label="粘贴", command=lambda: print("粘贴"))
# 绑定右键事件
root.bind("<Button-3>", show_context_menu)
对话框
消息框
from tkinter import messagebox
# 信息框
messagebox.showinfo("提示", "这是一个信息提示")
# 警告框
messagebox.showwarning("警告", "这是一个警告信息")
# 错误框
messagebox.showerror("错误", "发生了一个错误")
# 询问框
result = messagebox.askokcancel("确认", "确定要删除吗?")
if result:
print("用户点击了确定")
# 是/否框
result = messagebox.askyesno("选择", "是否继续?")
if result:
print("用户选择了是")
# 重试/取消框
result = messagebox.askretrycancel("重试", "操作失败,是否重试?")
文件对话框
from tkinter import filedialog
# 打开文件对话框
file_path = filedialog.askopenfilename(
title="选择文件",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
# 保存文件对话框
save_path = filedialog.asksaveasfilename(
title="保存文件",
defaultextension=".txt",
filetypes=[("文本文件", "*.txt")]
)
# 选择目录
directory = filedialog.askdirectory(title="选择目录")
颜色选择器
from tkinter import colorchooser
# 选择颜色
color = colorchooser.askcolor(title="选择颜色")
if color[1]: # color[1] 是十六进制颜色值
selected_color = color[1]
print(f"选择的颜色: {selected_color}")
事件处理
绑定事件
# 按键事件
def on_key_press(event):
print(f"按键: {event.keysym}")
root.bind("<KeyPress>", on_key_press)
root.bind("<Return>", lambda e: print("回车键"))
root.bind("<Escape>", lambda e: root.quit())
# 鼠标事件
def on_mouse_click(event):
print(f"鼠标点击位置: ({event.x}, {event.y})")
def on_mouse_motion(event):
print(f"鼠标移动: ({event.x}, {event.y})")
button.bind("<Button-1>", on_mouse_click) # 左键点击
button.bind("<B1-Motion>", on_mouse_motion) # 左键拖动
button.bind("<Enter>", lambda e: print("鼠标进入"))
button.bind("<Leave>", lambda e: print("鼠标离开"))
# 窗口事件
def on_window_resize(event):
print(f"窗口大小: {event.width}x{event.height}")
root.bind("<Configure>", on_window_resize)
事件参数
def event_info(event):
print(f"事件类型: {event.type}")
print(f"组件: {event.widget}")
print(f"时间: {event.time}")
print(f"坐标: ({event.x}, {event.y})")
print(f"按键: {event.keysym}")
print(f"状态: {event.state}")
# 绑定多个事件
widget.bind("<Button-1>", event_info)
widget.bind("<KeyPress>", event_info)
多窗口管理
Toplevel 顶层窗口
def open_new_window():
# 创建新窗口
new_window = tk.Toplevel(root)
new_window.title("新窗口")
new_window.geometry("300x200")
# 在新窗口中添加组件
label = tk.Label(new_window, text="这是一个新窗口")
label.pack(pady=20)
close_button = tk.Button(
new_window,
text="关闭窗口",
command=new_window.destroy
)
close_button.pack(pady=10)
# 主窗口中的按钮
open_btn = tk.Button(root, text="打开新窗口", command=open_new_window)
open_btn.pack(pady=20)
窗口间通信
class MainWindow:
def __init__(self):
self.root = tk.Tk()
self.root.title("主窗口")
self.data = "共享数据"
tk.Button(self.root, text="打开子窗口", command=self.open_child).pack()
def open_child(self):
ChildWindow(self)
def update_data(self, new_data):
self.data = new_data
print(f"数据已更新: {self.data}")
class ChildWindow:
def __init__(self, parent):
self.parent = parent
self.window = tk.Toplevel(parent.root)
self.window.title("子窗口")
tk.Label(self.window, text=f"当前数据: {parent.data}").pack()
entry = tk.Entry(self.window)
entry.pack()
tk.Button(
self.window,
text="更新数据",
command=lambda: self.update_data(entry.get())
).pack()
def update_data(self, new_data):
self.parent.update_data(new_data)
self.window.destroy()
app = MainWindow()
app.root.mainloop()
样式美化
ttk 主题
import tkinter.ttk as ttk
# 获取可用主题
print(ttk.Style().theme_names())
# 设置主题
style = ttk.Style()
style.theme_use("clam") # clam, alt, default, classic
# 自定义样式
style.configure("Custom.TButton",
font=("Arial", 12),
foreground="white",
background="blue"
)
# 使用自定义样式
button = ttk.Button(root, text="自定义按钮", style="Custom.TButton")
button.pack(pady=10)
颜色和字体
# 颜色定义
colors = {
'bg': '#f0f0f0', # 背景色
'fg': '#333333', # 前景色
'button_bg': '#4CAF50', # 按钮背景
'button_fg': 'white', # 按钮前景
'accent': '#2196F3' # 强调色
}
# 字体定义
fonts = {
'title': ('Arial', 16, 'bold'),
'normal': ('Arial', 10),
'button': ('Arial', 10, 'bold')
}
# 应用样式
title_label = tk.Label(
root,
text="标题",
font=fonts['title'],
fg=colors['fg'],
bg=colors['bg']
)
边框和效果
# 边框样式
relief_options = [tk.FLAT, tk.RAISED, tk.SUNKEN, tk.GROOVE, tk.RIDGE]
for i, relief in enumerate(relief_options):
label = tk.Label(
root,
text=relief,
relief=relief,
bd=3, # 边框宽度
padx=10,
pady=5
)
label.grid(row=i, column=0, pady=5)
# 创建3D效果
frame = tk.Frame(root, relief=tk.RAISED, bd=2)
frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
content = tk.Label(frame, text="3D边框效果", bg="white")
content.pack(padx=5, pady=5)
实用示例
计算器程序
class Calculator:
def __init__(self):
self.root = tk.Tk()
self.root.title("计算器")
self.root.geometry("300x400")
self.root.resizable(False, False)
self.current = ""
self.result_var = tk.StringVar(value="0")
self.create_widgets()
def create_widgets(self):
# 显示屏
display = tk.Entry(
self.root,
textvariable=self.result_var,
font=('Arial', 20),
justify='right',
bd=10,
readonlybackground='white',
state='readonly'
)
display.pack(fill=tk.BOTH, padx=10, pady=10)
# 按钮布局
buttons = [
['C', '±', '%', '÷'],
['7', '8', '9', '×'],
['4', '5', '6', '-'],
['1', '2', '3', '+'],
['0', '.', '=']
]
for row_idx, row in enumerate(buttons):
for col_idx, text in enumerate(row):
if text == '0':
btn = tk.Button(
self.root, text=text,
font=('Arial', 14), height=2,
command=lambda t=text: self.on_click(t)
)
btn.grid(row=row_idx+1, column=col_idx,
columnspan=2, sticky='nsew', padx=2, pady=2)
elif text == '=':
btn = tk.Button(
self.root, text=text,
font=('Arial', 14), height=2,
bg='orange', fg='white',
command=lambda t=text: self.on_click(t)
)
btn.grid(row=row_idx+1, column=col_idx,
columnspan=2, sticky='nsew', padx=2, pady=2)
else:
color = 'lightgray' if text in 'C±%÷×+-' else 'white'
btn = tk.Button(
self.root, text=text,
font=('Arial', 14), height=2,
bg=color,
command=lambda t=text: self.on_click(t)
)
btn.grid(row=row_idx+1, column=col_idx,
sticky='nsew', padx=2, pady=2)
# 配置网格权重
for i in range(5):
self.root.grid_rowconfigure(i+1, weight=1)
for i in range(4):
self.root.grid_columnconfigure(i, weight=1)
def on_click(self, text):
if text == 'C':
self.current = ""
self.result_var.set("0")
elif text == '=':
try:
# 替换运算符
expression = self.current.replace('×', '*').replace('÷', '/')
result = eval(expression)
self.result_var.set(str(result))
self.current = str(result)
except:
self.result_var.set("错误")
self.current = ""
elif text == '±':
if self.current and self.current != '0':
if self.current[0] == '-':
self.current = self.current[1:]
else:
self.current = '-' + self.current
self.result_var.set(self.current)
else:
if self.result_var.get() == "0" or self.result_var.get() == "错误":
self.current = text
else:
self.current += text
self.result_var.set(self.current)
def run(self):
self.root.mainloop()
# 运行计算器
if __name__ == "__main__":
calc = Calculator()
calc.run()
待办事项应用
class TodoApp:
def __init__(self):
self.root = tk.Tk()
self.root.title("待办事项")
self.root.geometry("400x500")
self.todos = []
self.create_widgets()
def create_widgets(self):
# 标题
title = tk.Label(
self.root,
text="我的待办事项",
font=('Arial', 16, 'bold'),
bg='#4CAF50',
fg='white',
pady=10
)
title.pack(fill=tk.X)
# 输入框架
input_frame = tk.Frame(self.root)
input_frame.pack(fill=tk.X, padx=10, pady=10)
self.entry = tk.Entry(
input_frame,
font=('Arial', 12),
bd=2,
relief=tk.GROOVE
)
self.entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
add_btn = tk.Button(
input_frame,
text="添加",
command=self.add_todo,
bg='#4CAF50',
fg='white',
font=('Arial', 10, 'bold'),
padx=20
)
add_btn.pack(side=tk.RIGHT)
# 绑定回车键
self.entry.bind('<Return>', lambda e: self.add_todo())
# 列表框架
list_frame = tk.Frame(self.root)
list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
# 滚动条
scrollbar = tk.Scrollbar(list_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 列表框
self.listbox = tk.Listbox(
list_frame,
font=('Arial', 11),
bd=0,
highlightthickness=0,
selectbackground='#a6a6a6',
activestyle="none",
yscrollcommand=scrollbar.set
)
self.listbox.pack(fill=tk.BOTH, expand=True)
scrollbar.config(command=self.listbox.yview)
# 按钮框架
button_frame = tk.Frame(self.root)
button_frame.pack(fill=tk.X, padx=10, pady=(0, 10))
complete_btn = tk.Button(
button_frame,
text="完成",
command=self.complete_todo,
bg='#2196F3',
fg='white',
font=('Arial', 10, 'bold')
)
complete_btn.pack(side=tk.LEFT, padx=(0, 5))
delete_btn = tk.Button(
button_frame,
text="删除",
command=self.delete_todo,
bg='#f44336',
fg='white',
font=('Arial', 10, 'bold')
)
delete_btn.pack(side=tk.LEFT, padx=5)
clear_btn = tk.Button(
button_frame,
text="清空",
command=self.clear_all,
bg='#FF9800',
fg='white',
font=('Arial', 10, 'bold')
)
clear_btn.pack(side=tk.RIGHT)
def add_todo(self):
todo = self.entry.get().strip()
if todo:
self.listbox.insert(tk.END, f"□ {todo}")
self.todos.append(todo)
self.entry.delete(0, tk.END)
def complete_todo(self):
selection = self.listbox.curselection()
if selection:
index = selection[0]
current_text = self.listbox.get(index)
if current_text.startswith("□"):
new_text = current_text.replace("□", "☑", 1)
else:
new_text = current_text.replace("☑", "□", 1)
self.listbox.delete(index)
self.listbox.insert(index, new_text)
def delete_todo(self):
selection = self.listbox.curselection()
if selection:
index = selection[0]
self.listbox.delete(index)
del self.todos[index]
def clear_all(self):
self.listbox.delete(0, tk.END)
self.todos.clear()
def run(self):
self.root.mainloop()
# 运行待办事项应用
if __name__ == "__main__":
app = TodoApp()
app.run()
最佳实践
代码组织
# 使用类组织代码
class Application:
def __init__(self):
self.root = tk.Tk()
self.setup_ui()
def setup_ui(self):
# 设置UI组件
pass
def run(self):
self.root.mainloop()
# 分离UI和逻辑
class UI:
def __init__(self, root):
self.root = root
self.create_widgets()
def create_widgets(self):
pass
class Logic:
def __init__(self, ui):
self.ui = ui
def process_data(self):
pass
错误处理
def safe_operation():
try:
# 可能出错的操作
result = risky_function()
return result
except ValueError as e:
messagebox.showerror("错误", f"数值错误: {e}")
except FileNotFoundError as e:
messagebox.showerror("错误", f"文件未找到: {e}")
except Exception as e:
messagebox.showerror("错误", f"未知错误: {e}")
finally:
# 清理操作
pass
性能优化
# 使用after代替循环
def update_clock():
current_time = time.strftime("%H:%M:%S")
clock_label.config(text=current_time)
root.after(1000, update_clock) # 1秒后再次调用
# 批量更新UI
def update_multiple_widgets():
widgets = [widget1, widget2, widget3]
for widget in widgets:
widget.config(state='disabled')
# 执行耗时操作
for widget in widgets:
widget.config(state='normal')