python中tkinter的详细用法
Python Tkinter 详细用法指南
Tkinter 是 Python 的标准 GUI 库,可以快速创建桌面应用程序。以下是 Tkinter 的全面详细教程。
1. Tkinter 基础
安装和导入
# Tkinter 是 Python 标准库,无需安装
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from tkinter import filedialog
第一个 Tkinter 程序
import tkinter as tk
# 创建主窗口
root = tk.Tk()
root.title("我的第一个 Tkinter 程序")
root.geometry("400x300") # 宽度x高度
# 添加一个标签
label = tk.Label(root, text="Hello, Tkinter!")
label.pack()
# 运行主循环
root.mainloop()
2. 窗口和基础设置
窗口属性设置
import tkinter as tk
root = tk.Tk()
# 窗口标题
root.title("Tkinter 窗口示例")
# 窗口大小和位置
root.geometry("500x400") # 宽x高
root.geometry("500x400+100+50") # 宽x高+x位置+y位置
# 窗口最小最大尺寸
root.minsize(300, 200) # 最小尺寸
root.maxsize(800, 600) # 最大尺寸
# 窗口图标(需要 .ico 文件)
# root.iconbitmap("icon.ico")
# 窗口背景色
root.configure(bg='lightblue')
# 窗口始终置顶
root.attributes('-topmost', True)
# 透明度(0.0-1.0)
root.attributes('-alpha', 0.9)
root.mainloop()
多个窗口管理
import tkinter as tk
class MultiWindowApp:
def __init__(self):
self.root = tk.Tk()
self.root.title("多窗口示例")
self.root.geometry("400x300")
# 创建打开新窗口的按钮
btn_new_window = tk.Button(self.root, text="打开新窗口", command=self.open_new_window)
btn_new_window.pack(pady=20)
def open_new_window(self):
# 创建顶级窗口
new_window = tk.Toplevel(self.root)
new_window.title("新窗口")
new_window.geometry("300x200")
label = tk.Label(new_window, text="这是一个新窗口")
label.pack(pady=20)
btn_close = tk.Button(new_window, text="关闭", command=new_window.destroy)
btn_close.pack()
if __name__ == "__main__":
app = MultiWindowApp()
app.root.mainloop()
3. 常用组件详解
Label 标签
import tkinter as tk
root = tk.Tk()
root.title("Label 示例")
root.geometry("400x200")
# 基本标签
label1 = tk.Label(root, text="普通标签")
label1.pack()
# 带样式的标签
label2 = tk.Label(
root,
text="带样式的标签",
font=("Arial", 16, "bold"),
fg="white",
bg="blue",
padx=20,
pady=10,
relief="raised", # 边框样式
bd=3 # 边框宽度
)
label2.pack(pady=10)
# 多行文本标签
label3 = tk.Label(
root,
text="这是多行文本标签\n第二行内容\n第三行内容",
justify="left", # 对齐方式
wraplength=200 # 自动换行宽度
)
label3.pack(pady=10)
root.mainloop()
Button 按钮
import tkinter as tk
from tkinter import messagebox
class ButtonExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Button 示例")
self.root.geometry("400x300")
self.counter = 0
self.create_widgets()
def create_widgets(self):
# 基本按钮
btn1 = tk.Button(self.root, text="点击我", command=self.button_click)
btn1.pack(pady=10)
# 带样式的按钮
btn2 = tk.Button(
self.root,
text="样式按钮",
command=self.styled_button_click,
font=("Arial", 12),
bg="green",
fg="white",
activebackground="darkgreen",
activeforeground="white",
width=15,
height=2
)
btn2.pack(pady=10)
# 禁用/启用按钮
self.toggle_btn = tk.Button(
self.root,
text="禁用/启用",
command=self.toggle_button
)
self.toggle_btn.pack(pady=10)
# 计数标签
self.counter_label = tk.Label(self.root, text=f"点击次数: {self.counter}")
self.counter_label.pack(pady=10)
# 带图标的按钮(需要图片文件)
# self.photo = tk.PhotoImage(file="icon.png")
# btn_image = tk.Button(self.root, image=self.photo, command=self.image_button_click)
# btn_image.pack(pady=10)
def button_click(self):
self.counter += 1
self.counter_label.config(text=f"点击次数: {self.counter}")
messagebox.showinfo("信息", f"按钮被点击了!\n当前计数: {self.counter}")
def styled_button_click(self):
messagebox.showinfo("样式按钮", "这是带样式的按钮!")
def toggle_button(self):
if self.toggle_btn['state'] == tk.NORMAL:
self.toggle_btn.config(state=tk.DISABLED, text="按钮已禁用")
else:
self.toggle_btn.config(state=tk.NORMAL, text="禁用/启用")
if __name__ == "__main__":
app = ButtonExample()
app.root.mainloop()
Entry 输入框
import tkinter as tk
from tkinter import messagebox
class EntryExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Entry 示例")
self.root.geometry("400x300")
self.create_widgets()
def create_widgets(self):
# 基本输入框
tk.Label(self.root, text="用户名:").pack(pady=5)
self.entry_username = tk.Entry(self.root, width=30)
self.entry_username.pack(pady=5)
# 密码输入框(显示*)
tk.Label(self.root, text="密码:").pack(pady=5)
self.entry_password = tk.Entry(self.root, width=30, show="*")
self.entry_password.pack(pady=5)
# 带默认文本的输入框
tk.Label(self.root, text="邮箱:").pack(pady=5)
self.entry_email = tk.Entry(self.root, width=30)
self.entry_email.insert(0, "example@email.com") # 插入默认文本
self.entry_email.pack(pady=5)
# 只读输入框
tk.Label(self.root, text="只读字段:").pack(pady=5)
self.entry_readonly = tk.Entry(self.root, width=30, state='readonly')
self.entry_readonly.pack(pady=5)
# 按钮
btn_submit = tk.Button(self.root, text="提交", command=self.submit)
btn_submit.pack(pady=20)
btn_clear = tk.Button(self.root, text="清空", command=self.clear)
btn_clear.pack(pady=5)
def submit(self):
username = self.entry_username.get()
password = self.entry_password.get()
email = self.entry_email.get()
if not username or not password:
messagebox.showerror("错误", "用户名和密码不能为空!")
return
messagebox.showinfo("提交成功",
f"用户名: {username}\n"
f"密码: {'*' * len(password)}\n"
f"邮箱: {email}")
def clear(self):
self.entry_username.delete(0, tk.END)
self.entry_password.delete(0, tk.END)
self.entry_email.delete(0, tk.END)
self.entry_email.insert(0, "example@email.com")
if __name__ == "__main__":
app = EntryExample()
app.root.mainloop()
Text 文本框
import tkinter as tk
class TextExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Text 文本框示例")
self.root.geometry("500x400")
self.create_widgets()
def create_widgets(self):
# 创建文本框
self.text = tk.Text(
self.root,
width=50,
height=15,
wrap=tk.WORD, # 自动换行
font=("Arial", 12)
)
self.text.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
# 添加滚动条
scrollbar = tk.Scrollbar(self.text)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.text.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.text.yview)
# 按钮框架
button_frame = tk.Frame(self.root)
button_frame.pack(pady=10)
# 各种操作按钮
btn_insert = tk.Button(button_frame, text="插入文本", command=self.insert_text)
btn_insert.grid(row=0, column=0, padx=5)
btn_get = tk.Button(button_frame, text="获取文本", command=self.get_text)
btn_get.grid(row=0, column=1, padx=5)
btn_clear = tk.Button(button_frame, text="清空", command=self.clear_text)
btn_clear.grid(row=0, column=2, padx=5)
btn_delete = tk.Button(button_frame, text="删除选中", command=self.delete_selection)
btn_delete.grid(row=0, column=3, padx=5)
# 插入一些示例文本
self.insert_sample_text()
def insert_sample_text(self):
sample_text = """这是一个 Text 组件示例。
Text 组件可以显示多行文本,并支持以下功能:
• 文本格式化
• 文本搜索
• 文本选择和编辑
• 滚动显示
你可以在这里输入任意多行文本!"""
self.text.insert(tk.END, sample_text)
def insert_text(self):
self.text.insert(tk.END, "\n\n插入的新文本!")
def get_text(self):
content = self.text.get(1.0, tk.END) # 从第1行第0字符到末尾
print("文本框内容:")
print(content)
def clear_text(self):
self.text.delete(1.0, tk.END)
def delete_selection(self):
try:
# 删除选中的文本
self.text.delete(tk.SEL_FIRST, tk.SEL_LAST)
except tk.TclError:
# 如果没有选中文本
pass
if __name__ == "__main__":
app = TextExample()
app.root.mainloop()
4. 布局管理
pack 布局
import tkinter as tk
class PackLayoutExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Pack 布局示例")
self.root.geometry("400x300")
self.create_widgets()
def create_widgets(self):
# 顶部标签
top_label = tk.Label(self.root, text="顶部", bg="red", fg="white", height=2)
top_label.pack(fill=tk.X, padx=10, pady=5)
# 左侧框架
left_frame = tk.Frame(self.root, bg="lightblue", width=150)
left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
# 左侧按钮
for i in range(5):
btn = tk.Button(left_frame, text=f"按钮 {i+1}")
btn.pack(fill=tk.X, padx=5, pady=2)
# 右侧框架
right_frame = tk.Frame(self.root, bg="lightgreen")
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 右侧内容
label1 = tk.Label(right_frame, text="右侧内容区域", bg="yellow")
label1.pack(fill=tk.X, pady=5)
text = tk.Text(right_frame, height=5)
text.pack(fill=tk.BOTH, expand=True, pady=5)
# 底部框架
bottom_frame = tk.Frame(self.root, bg="lightgray", height=50)
bottom_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=5)
# 底部按钮
btn_ok = tk.Button(bottom_frame, text="确定")
btn_ok.pack(side=tk.RIGHT, padx=5)
btn_cancel = tk.Button(bottom_frame, text="取消")
btn_cancel.pack(side=tk.RIGHT, padx=5)
if __name__ == "__main__":
app = PackLayoutExample()
app.root.mainloop()
grid 布局
import tkinter as tk
class GridLayoutExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Grid 布局示例")
self.root.geometry("500x400")
self.create_widgets()
def create_widgets(self):
# 标题
title_label = tk.Label(self.root, text="用户注册表单", font=("Arial", 16, "bold"))
title_label.grid(row=0, column=0, columnspan=2, pady=20)
# 用户名
tk.Label(self.root, text="用户名:").grid(row=1, column=0, sticky=tk.E, padx=5, pady=5)
entry_username = tk.Entry(self.root, width=30)
entry_username.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)
# 密码
tk.Label(self.root, text="密码:").grid(row=2, column=0, sticky=tk.E, padx=5, pady=5)
entry_password = tk.Entry(self.root, width=30, show="*")
entry_password.grid(row=2, column=1, sticky=tk.W, padx=5, pady=5)
# 邮箱
tk.Label(self.root, text="邮箱:").grid(row=3, column=0, sticky=tk.E, padx=5, pady=5)
entry_email = tk.Entry(self.root, width=30)
entry_email.grid(row=3, column=1, sticky=tk.W, padx=5, pady=5)
# 性别(使用框架)
tk.Label(self.root, text="性别:").grid(row=4, column=0, sticky=tk.E, padx=5, pady=5)
gender_frame = tk.Frame(self.root)
gender_frame.grid(row=4, column=1, sticky=tk.W, padx=5, pady=5)
gender_var = tk.StringVar(value="male")
tk.Radiobutton(gender_frame, text="男", variable=gender_var, value="male").pack(side=tk.LEFT)
tk.Radiobutton(gender_frame, text="女", variable=gender_var, value="female").pack(side=tk.LEFT)
# 爱好
tk.Label(self.root, text="爱好:").grid(row=5, column=0, sticky=tk.NE, padx=5, pady=5)
hobbies_frame = tk.Frame(self.root)
hobbies_frame.grid(row=5, column=1, sticky=tk.W, padx=5, pady=5)
hobbies_vars = []
hobbies = ["阅读", "运动", "音乐", "旅游"]
for i, hobby in enumerate(hobbies):
var = tk.BooleanVar()
cb = tk.Checkbutton(hobbies_frame, text=hobby, variable=var)
cb.grid(row=i//2, column=i%2, sticky=tk.W)
hobbies_vars.append((hobby, var))
# 按钮
button_frame = tk.Frame(self.root)
button_frame.grid(row=6, column=0, columnspan=2, pady=20)
tk.Button(button_frame, text="提交", width=10).pack(side=tk.LEFT, padx=10)
tk.Button(button_frame, text="重置", width=10).pack(side=tk.LEFT, padx=10)
tk.Button(button_frame, text="取消", width=10).pack(side=tk.LEFT, padx=10)
# 配置行列权重,使组件可以随窗口缩放
self.root.grid_rowconfigure(5, weight=1)
self.root.grid_columnconfigure(1, weight=1)
if __name__ == "__main__":
app = GridLayoutExample()
app.root.mainloop()
place 布局
import tkinter as tk
class PlaceLayoutExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Place 布局示例")
self.root.geometry("500x400")
self.create_widgets()
def create_widgets(self):
# 使用绝对坐标放置组件
label1 = tk.Label(self.root, text="绝对位置 (50, 50)", bg="lightblue")
label1.place(x=50, y=50)
# 相对位置放置
label2 = tk.Label(self.root, text="相对位置 (0.5, 0.2)", bg="lightgreen")
label2.place(relx=0.5, rely=0.2, anchor=tk.CENTER)
# 指定大小
label3 = tk.Label(self.root, text="指定大小", bg="lightyellow")
label3.place(x=100, y=150, width=200, height=50)
# 组合使用
label4 = tk.Label(self.root, text="组合使用", bg="lightcoral")
label4.place(relx=0.8, rely=0.8, anchor=tk.SE, width=100, height=30)
# 动态更新的标签
self.dynamic_label = tk.Label(self.root, text="动态位置", bg="orange")
self.dynamic_label.place(x=200, y=250)
# 移动按钮
btn_move = tk.Button(self.root, text="移动标签", command=self.move_label)
btn_move.place(relx=0.5, rely=0.9, anchor=tk.CENTER)
def move_label(self):
import random
x = random.randint(50, 350)
y = random.randint(50, 300)
self.dynamic_label.place(x=x, y=y)
if __name__ == "__main__":
app = PlaceLayoutExample()
app.root.mainloop()
5. 高级组件
下拉列表 (Combobox)
import tkinter as tk
from tkinter import ttk
class ComboboxExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Combobox 示例")
self.root.geometry("400x300")
self.create_widgets()
def create_widgets(self):
# 基本下拉列表
tk.Label(self.root, text="选择城市:").pack(pady=10)
self.city_var = tk.StringVar()
cities = ["北京", "上海", "广州", "深圳", "杭州", "成都", "武汉"]
self.combo_city = ttk.Combobox(
self.root,
textvariable=self.city_var,
values=cities,
state="readonly" # 只能选择,不能输入
)
self.combo_city.pack(pady=5)
self.combo_city.set("请选择城市") # 设置默认文本
# 绑定选择事件
self.combo_city.bind('<<ComboboxSelected>>', self.on_city_selected)
# 显示选择的城市
self.result_label = tk.Label(self.root, text="", font=("Arial", 12))
self.result_label.pack(pady=20)
# 可编辑的下拉列表
tk.Label(self.root, text="自定义输入:").pack(pady=10)
self.custom_var = tk.StringVar()
combo_custom = ttk.Combobox(
self.root,
textvariable=self.custom_var,
values=["选项1", "选项2", "选项3"]
)
combo_custom.pack(pady=5)
btn_show_custom = tk.Button(self.root, text="显示输入", command=self.show_custom)
btn_show_custom.pack(pady=10)
def on_city_selected(self, event):
selected_city = self.city_var.get()
self.result_label.config(text=f"您选择的城市是: {selected_city}")
def show_custom(self):
custom_value = self.custom_var.get()
messagebox.showinfo("自定义输入", f"您输入的值是: {custom_value}")
if __name__ == "__main__":
app = ComboboxExample()
app.root.mainloop()
列表框 (Listbox)
import tkinter as tk
from tkinter import messagebox
class ListboxExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("Listbox 示例")
self.root.geometry("500x400")
self.create_widgets()
def create_widgets(self):
# 左侧列表框
left_frame = tk.Frame(self.root)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)
tk.Label(left_frame, text="项目列表").pack()
self.listbox = tk.Listbox(left_frame, selectmode=tk.EXTENDED)
self.listbox.pack(fill=tk.BOTH, expand=True, pady=5)
# 添加滚动条
scrollbar = tk.Scrollbar(self.listbox)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.listbox.yview)
# 添加一些示例项目
for i in range(20):
self.listbox.insert(tk.END, f"项目 {i+1}")
# 右侧操作按钮
right_frame = tk.Frame(self.root)
right_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10, pady=10)
tk.Button(right_frame, text="添加项目", command=self.add_item).pack(fill=tk.X, pady=5)
tk.Button(right_frame, text="删除选中", command=self.delete_selected).pack(fill=tk.X, pady=5)
tk.Button(right_frame, text="清空列表", command=self.clear_list).pack(fill=tk.X, pady=5)
tk.Button(right_frame, text="获取选中", command=self.get_selected).pack(fill=tk.X, pady=5)
tk.Button(right_frame, text="排序", command=self.sort_list).pack(fill=tk.X, pady=5)
# 添加项目输入框
self.entry_new_item = tk.Entry(right_frame)
self.entry_new_item.pack(fill=tk.X, pady=5)
self.entry_new_item.bind('<Return>', lambda e: self.add_item())
def add_item(self):
new_item = self.entry_new_item.get().strip()
if new_item:
self.listbox.insert(tk.END, new_item)
self.entry_new_item.delete(0, tk.END)
def delete_selected(self):
selected_indices = self.listbox.curselection()
# 从后往前删除,避免索引变化
for index in selected_indices[::-1]:
self.listbox.delete(index)
def clear_list(self):
self.listbox.delete(0, tk.END)
def get_selected(self):
selected_indices = self.listbox.curselection()
selected_items = [self.listbox.get(i) for i in selected_indices]
if selected_items:
messagebox.showinfo("选中的项目", "\n".join(selected_items))
else:
messagebox.showinfo("提示", "没有选中任何项目")
def sort_list(self):
# 获取所有项目并排序
items = list(self.listbox.get(0, tk.END))
items.sort()
# 清空并重新插入排序后的项目
self.listbox.delete(0, tk.END)
for item in items:
self.listbox.insert(tk.END, item)
if __name__ == "__main__":
app = ListboxExample()
app.root.mainloop()
进度条 (Progressbar)
import tkinter as tk
from tkinter import ttk
import time
import threading
class ProgressbarExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("进度条示例")
self.root.geometry("400x200")
self.progress_value = 0
self.is_running = False
self.create_widgets()
def create_widgets(self):
# 确定进度条
tk.Label(self.root, text="确定进度条:").pack(pady=10)
self.determinate_progress = ttk.Progressbar(
self.root,
orient=tk.HORIZONTAL,
length=300,
mode='determinate'
)
self.determinate_progress.pack(pady=5)
btn_start_determinate = tk.Button(
self.root,
text="开始确定进度",
command=self.start_determinate_progress
)
btn_start_determinate.pack(pady=5)
# 不确定进度条
tk.Label(self.root, text="不确定进度条:").pack(pady=10)
self.indeterminate_progress = ttk.Progressbar(
self.root,
orient=tk.HORIZONTAL,
length=300,
mode='indeterminate'
)
self.indeterminate_progress.pack(pady=5)
btn_frame = tk.Frame(self.root)
btn_frame.pack(pady=10)
btn_start_indeterminate = tk.Button(
btn_frame,
text="开始不确定进度",
command=self.start_indeterminate_progress
)
btn_start_indeterminate.pack(side=tk.LEFT, padx=5)
btn_stop_indeterminate = tk.Button(
btn_frame,
text="停止",
command=self.stop_indeterminate_progress
)
btn_stop_indeterminate.pack(side=tk.LEFT, padx=5)
def start_determinate_progress(self):
def update_progress():
for i in range(101):
if not self.is_running:
break
self.determinate_progress['value'] = i
self.root.update()
time.sleep(0.05)
self.is_running = True
thread = threading.Thread(target=update_progress)
thread.daemon = True
thread.start()
def start_indeterminate_progress(self):
self.indeterminate_progress.start(10)
def stop_indeterminate_progress(self):
self.indeterminate_progress.stop()
self.is_running = False
if __name__ == "__main__":
app = ProgressbarExample()
app.root.mainloop()
6. 菜单和对话框
菜单系统
import tkinter as tk
from tkinter import messagebox, filedialog
class MenuExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("菜单系统示例")
self.root.geometry("500x400")
self.create_menus()
self.create_content()
def create_menus(self):
# 创建菜单栏
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
# 文件菜单
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")
file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
file_menu.add_separator()
file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
file_menu.add_command(label="另存为", command=self.save_as_file)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.root.quit)
# 编辑菜单
edit_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="编辑", menu=edit_menu)
edit_menu.add_command(label="撤销", command=self.undo)
edit_menu.add_command(label="重做", command=self.redo)
edit_menu.add_separator()
edit_menu.add_command(label="复制", command=self.copy)
edit_menu.add_command(label="粘贴", command=self.paste)
# 视图菜单
view_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="视图", menu=view_menu)
self.theme_var = tk.StringVar(value="light")
view_menu.add_radiobutton(label="浅色主题", variable=self.theme_var, value="light", command=self.change_theme)
view_menu.add_radiobutton(label="深色主题", variable=self.theme_var, value="dark", command=self.change_theme)
# 帮助菜单
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="帮助", menu=help_menu)
help_menu.add_command(label="关于", command=self.about)
# 添加快捷键
self.root.bind('<Control-n>', lambda e: self.new_file())
self.root.bind('<Control-o>', lambda e: self.open_file())
self.root.bind('<Control-s>', lambda e: self.save_file())
def create_content(self):
# 创建文本区域
self.text_area = tk.Text(self.root, wrap=tk.WORD)
self.text_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 添加滚动条
scrollbar = tk.Scrollbar(self.text_area)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.text_area.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.text_area.yview)
def new_file(self):
self.text_area.delete(1.0, tk.END)
messagebox.showinfo("新建", "新建文件")
def open_file(self):
file_path = filedialog.askopenfilename(
title="选择文件",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if file_path:
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
self.text_area.delete(1.0, tk.END)
self.text_area.insert(tk.END, content)
messagebox.showinfo("成功", f"已打开文件: {file_path}")
except Exception as e:
messagebox.showerror("错误", f"无法打开文件: {e}")
def save_file(self):
messagebox.showinfo("保存", "保存文件")
def save_as_file(self):
messagebox.showinfo("另存为", "另存为文件")
def undo(self):
try:
self.text_area.edit_undo()
except tk.TclError:
pass
def redo(self):
try:
self.text_area.edit_redo()
except tk.TclError:
pass
def copy(self):
try:
self.root.clipboard_clear()
self.root.clipboard_append(self.text_area.selection_get())
except tk.TclError:
pass
def paste(self):
try:
self.text_area.insert(tk.INSERT, self.root.clipboard_get())
except tk.TclError:
pass
def change_theme(self):
theme = self.theme_var.get()
if theme == "light":
self.text_area.config(bg="white", fg="black")
else:
self.text_area.config(bg="black", fg="white")
def about(self):
messagebox.showinfo("关于", "Tkinter 菜单系统示例\n版本 1.0")
if __name__ == "__main__":
app = MenuExample()
app.root.mainloop()
对话框使用
import tkinter as tk
from tkinter import messagebox, filedialog, colorchooser, simpledialog
class DialogExample:
def __init__(self):
self.root = tk.Tk()
self.root.title("对话框示例")
self.root.geometry("500x400")
self.create_widgets()
def create_widgets(self):
# 消息对话框按钮
message_frame = tk.LabelFrame(self.root, text="消息对话框", padx=10, pady=10)
message_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Button(message_frame, text="显示信息", command=self.show_info).pack(side=tk.LEFT, padx=5)
tk.Button(message_frame, text="显示警告", command=self.show_warning).pack(side=tk.LEFT, padx=5)
tk.Button(message_frame, text="显示错误", command=self.show_error).pack(side=tk.LEFT, padx=5)
tk.Button(message_frame, text="询问问题", command=self.ask_question).pack(side=tk.LEFT, padx=5)
tk.Button(message_frame, text="是/否选择", command=self.ask_yesno).pack(side=tk.LEFT, padx=5)
# 文件对话框按钮
file_frame = tk.LabelFrame(self.root, text="文件对话框", padx=10, pady=10)
file_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Button(file_frame, text="打开文件", command=self.open_file).pack(side=tk.LEFT, padx=5)
tk.Button(file_frame, text="保存文件", command=self.save_file).pack(side=tk.LEFT, padx=5)
tk.Button(file_frame, text="选择目录", command=self.select_directory).pack(side=tk.LEFT, padx=5)
# 其他对话框按钮
other_frame = tk.LabelFrame(self.root, text="其他对话框", padx=10, pady=10)
other_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Button(other_frame, text="选择颜色", command=self.choose_color).pack(side=tk.LEFT, padx=5)
tk.Button(other_frame, text="输入文本", command=self.input_text).pack(side=tk.LEFT, padx=5)
tk.Button(other_frame, text="输入整数", command=self.input_integer).pack(side=tk.LEFT, padx=5)
tk.Button(other_frame, text="输入浮点数", command=self.input_float).pack(side=tk.LEFT, padx=5)
# 结果显示区域
result_frame = tk.LabelFrame(self.root, text="对话框结果", padx=10, pady=10)
result_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
self.result_text = tk.Text(result_frame, height=10)
self.result_text.pack(fill=tk.BOTH, expand=True)
# 清空按钮
tk.Button(self.root, text="清空结果", command=self.clear_results).pack(pady=10)
def log_result(self, dialog_type, result):
self.result_text.insert(tk.END, f"{dialog_type}: {result}\n")
self.result_text.see(tk.END)
def show_info(self):
messagebox.showinfo("信息", "这是一个信息对话框")
self.log_result("信息对话框", "用户查看了信息")
def show_warning(self):
messagebox.showwarning("警告", "这是一个警告对话框")
self.log_result("警告对话框", "用户查看了警告")
def show_error(self):
messagebox.showerror("错误", "这是一个错误对话框")
self.log_result("错误对话框", "用户查看了错误")
def ask_question(self):
result = messagebox.askquestion("问题", "这是一个问题对话框?")
self.log_result("问题对话框", result)
def ask_yesno(self):
result = messagebox.askyesno("选择", "请选择是或否")
self.log_result("是/否对话框", result)
def open_file(self):
file_path = filedialog.askopenfilename(
title="选择文件",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
self.log_result("打开文件", file_path if file_path else "用户取消了选择")
def save_file(self):
file_path = filedialog.asksaveasfilename(
title="保存文件",
defaultextension=".txt",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
self.log_result("保存文件", file_path if file_path else "用户取消了保存")
def select_directory(self):
directory = filedialog.askdirectory(title="选择目录")
self.log_result("选择目录", directory if directory else "用户取消了选择")
def choose_color(self):
color = colorchooser.askcolor(title="选择颜色")
self.log_result("选择颜色", color)
def input_text(self):
text = simpledialog.askstring("输入文本", "请输入文本:")
self.log_result("输入文本", text if text else "用户取消了输入")
def input_integer(self):
number = simpledialog.askinteger("输入整数", "请输入整数:")
self.log_result("输入整数", number if number is not None else "用户取消了输入")
def input_float(self):
number = simpledialog.askfloat("输入浮点数", "请输入浮点数:")
self.log_result("输入浮点数", number if number is not None else "用户取消了输入")
def clear_results(self):
self.result_text.delete(1.0, tk.END)
if __name__ == "__main__":
app = DialogExample()
app.root.mainloop()
7. 综合实例:简单的文本编辑器
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext
import os
class SimpleTextEditor:
def __init__(self):
self.root = tk.Tk()
self.root.title("简单的文本编辑器")
self.root.geometry("800x600")
self.current_file = None
self.text_modified = False
self.create_menu()
self.create_toolbar()
self.create_text_area()
self.create_statusbar()
# 绑定修改事件
self.text_area.bind('<<Modified>>', self.on_text_modified)
def create_menu(self):
menubar = tk.Menu(self.root)
# 文件菜单
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")
file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
file_menu.add_command(label="另存为", command=self.save_as_file)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.quit_editor)
# 编辑菜单
edit_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="编辑", menu=edit_menu)
edit_menu.add_command(label="撤销", command=self.undo, accelerator="Ctrl+Z")
edit_menu.add_command(label="重做", command=self.redo, accelerator="Ctrl+Y")
edit_menu.add_separator()
edit_menu.add_command(label="剪切", command=self.cut, accelerator="Ctrl+X")
edit_menu.add_command(label="复制", command=self.copy, accelerator="Ctrl+C")
edit_menu.add_command(label="粘贴", command=self.paste, accelerator="Ctrl+V")
edit_menu.add_separator()
edit_menu.add_command(label="全选", command=self.select_all, accelerator="Ctrl+A")
# 视图菜单
view_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="视图", menu=view_menu)
self.font_size = tk.IntVar(value=12)
self.font_family = tk.StringVar(value="Arial")
font_menu = tk.Menu(view_menu, tearoff=0)
view_menu.add_cascade(label="字体", menu=font_menu)
# 字体大小
for size in [8, 10, 12, 14, 16, 18, 20, 24]:
font_menu.add_radiobutton(
label=str(size),
variable=self.font_size,
value=size,
command=self.change_font
)
self.root.config(menu=menubar)
# 绑定快捷键
self.root.bind('<Control-n>', lambda e: self.new_file())
self.root.bind('<Control-o>', lambda e: self.open_file())
self.root.bind('<Control-s>', lambda e: self.save_file())
self.root.bind('<Control-z>', lambda e: self.undo())
self.root.bind('<Control-y>', lambda e: self.redo())
self.root.bind('<Control-x>', lambda e: self.cut())
self.root.bind('<Control-c>', lambda e: self.copy())
self.root.bind('<Control-v>', lambda e: self.paste())
self.root.bind('<Control-a>', lambda e: self.select_all())
def create_toolbar(self):
toolbar = tk.Frame(self.root, relief=tk.RAISED, bd=1)
toolbar.pack(side=tk.TOP, fill=tk.X)
tk.Button(toolbar, text="新建", command=self.new_file).pack(side=tk.LEFT, padx=2, pady=2)
tk.Button(toolbar, text="打开", command=self.open_file).pack(side=tk.LEFT, padx=2, pady=2)
tk.Button(toolbar, text="保存", command=self.save_file).pack(side=tk.LEFT, padx=2, pady=2)
# 分隔符
ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, pady=2, fill=tk.Y)
tk.Button(toolbar, text="剪切", command=self.cut).pack(side=tk.LEFT, padx=2, pady=2)
tk.Button(toolbar, text="复制", command=self.copy).pack(side=tk.LEFT, padx=2, pady=2)
tk.Button(toolbar, text="粘贴", command=self.paste).pack(side=tk.LEFT, padx=2, pady=2)
# 字体大小选择
ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, pady=2, fill=tk.Y)
tk.Label(toolbar, text="字体大小:").pack(side=tk.LEFT, padx=2, pady=2)
font_size_combo = ttk.Combobox(
toolbar,
textvariable=self.font_size,
values=[8, 10, 12, 14, 16, 18, 20, 24],
width=5,
state="readonly"
)
font_size_combo.pack(side=tk.LEFT, padx=2, pady=2)
font_size_combo.bind('<<ComboboxSelected>>', lambda e: self.change_font())
def create_text_area(self):
self.text_area = scrolledtext.ScrolledText(
self.root,
wrap=tk.WORD,
font=("Arial", 12),
undo=True # 启用撤销功能
)
self.text_area.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.text_area.focus()
def create_statusbar(self):
self.statusbar = tk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)
def on_text_modified(self, event):
if self.text_area.edit_modified():
self.text_modified = True
filename = os.path.basename(self.current_file) if self.current_file else "未命名"
self.root.title(f"*{filename} - 简单的文本编辑器")
self.statusbar.config(text="已修改")
self.text_area.edit_modified(False)
def new_file(self):
if self.text_modified:
if not self.confirm_save():
return
self.text_area.delete(1.0, tk.END)
self.current_file = None
self.text_modified = False
self.root.title("未命名 - 简单的文本编辑器")
self.statusbar.config(text="新建文件")
def open_file(self):
if self.text_modified:
if not self.confirm_save():
return
file_path = filedialog.askopenfilename(
title="打开文件",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if file_path:
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
self.text_area.delete(1.0, tk.END)
self.text_area.insert(1.0, content)
self.current_file = file_path
self.text_modified = False
self.root.title(f"{os.path.basename(file_path)} - 简单的文本编辑器")
self.statusbar.config(text=f"已打开: {file_path}")
except Exception as e:
messagebox.showerror("错误", f"无法打开文件: {e}")
def save_file(self):
if self.current_file:
try:
with open(self.current_file, 'w', encoding='utf-8') as file:
content = self.text_area.get(1.0, tk.END)
file.write(content)
self.text_modified = False
self.root.title(f"{os.path.basename(self.current_file)} - 简单的文本编辑器")
self.statusbar.config(text=f"已保存: {self.current_file}")
messagebox.showinfo("成功", "文件保存成功")
except Exception as e:
messagebox.showerror("错误", f"无法保存文件: {e}")
else:
self.save_as_file()
def save_as_file(self):
file_path = filedialog.asksaveasfilename(
title="另存为",
defaultextension=".txt",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if file_path:
try:
with open(file_path, 'w', encoding='utf-8') as file:
content = self.text_area.get(1.0, tk.END)
file.write(content)
self.current_file = file_path
self.text_modified = False
self.root.title(f"{os.path.basename(file_path)} - 简单的文本编辑器")
self.statusbar.config(text=f"已保存: {file_path}")
messagebox.showinfo("成功", "文件保存成功")
except Exception as e:
messagebox.showerror("错误", f"无法保存文件: {e}")
def confirm_save(self):
result = messagebox.askyesnocancel(
"保存文件",
"文件已被修改,是否保存?"
)
if result is None: # 取消
return False
elif result: # 是
self.save_file()
return True
def quit_editor(self):
if self.text_modified:
if not self.confirm_save():
return
self.root.quit()
def undo(self):
try:
self.text_area.edit_undo()
except tk.TclError:
pass
def redo(self):
try:
self.text_area.edit_redo()
except tk.TclError:
pass
def cut(self):
try:
self.text_area.event_generate("<<Cut>>")
except tk.TclError:
pass
def copy(self):
try:
self.text_area.event_generate("<<Copy>>")
except tk.TclError:
pass
def paste(self):
try:
self.text_area.event_generate("<<Paste>>")
except tk.TclError:
pass
def select_all(self):
self.text_area.tag_add(tk.SEL, "1.0", tk.END)
self.text_area.mark_set(tk.INSERT, "1.0")
self.text_area.see(tk.INSERT)
def change_font(self):
font_size = self.font_size.get()
self.text_area.config(font=("Arial", font_size))
if __name__ == "__main__":
app = SimpleTextEditor()
app.root.mainloop()
8. 最佳实践和技巧
面向对象编程
import tkinter as tk
from tkinter import ttk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("面向对象的 Tkinter 应用")
self.geometry("600x400")
self.create_widgets()
self.setup_bindings()
def create_widgets(self):
# 使用 ttk 样式
style = ttk.Style()
style.configure('TButton', font=('Arial', 12))
# 主框架
main_frame = ttk.Frame(self, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 标签
self.label = ttk.Label(main_frame, text="欢迎使用 Tkinter!", font=('Arial', 16))
self.label.grid(row=0, column=0, columnspan=2, pady=10)
# 按钮
self.button = ttk.Button(main_frame, text="点击我", command=self.on_button_click)
self.button.grid(row=1, column=0, pady=5, sticky=tk.W)
self.quit_button = ttk.Button(main_frame, text="退出", command=self.quit)
self.quit_button.grid(row=1, column=1, pady=5, sticky=tk.E)
# 配置权重
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
main_frame.columnconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
def setup_bindings(self):
self.bind('<Escape>', lambda e: self.quit())
self.bind('<Return>', lambda e: self.on_button_click())
def on_button_click(self):
self.label.config(text="按钮被点击了!")
if __name__ == "__main__":
app = App()
app.mainloop()
主题和样式
import tkinter as tk
from tkinter import ttk
class ThemedApp:
def __init__(self):
self.root = tk.Tk()
self.root.title("主题应用")
self.root.geometry("500x400")
self.setup_theme()
self.create_widgets()
def setup_theme(self):
self.style = ttk.Style()
# 获取可用的主题
self.themes = self.style.theme_names()
self.current_theme = tk.StringVar(value=self.style.theme_use())
print(f"可用主题: {list(self.themes)}")
def create_widgets(self):
# 主题选择
theme_frame = ttk.LabelFrame(self.root, text="主题选择", padding="10")
theme_frame.pack(fill=tk.X, padx=10, pady=5)
for i, theme in enumerate(self.themes):
rb = ttk.Radiobutton(
theme_frame,
text=theme,
variable=self.current_theme,
value=theme,
command=self.change_theme
)
rb.grid(row=i//3, column=i%3, sticky=tk.W, padx=5, pady=2)
# 示例组件
demo_frame = ttk.LabelFrame(self.root, text="组件演示", padding="10")
demo_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
# 按钮
ttk.Button(demo_frame, text="普通按钮").pack(pady=5)
ttk.Button(demo_frame, text="主要按钮", style="Accent.TButton").pack(pady=5)
# 进度条
progress = ttk.Progressbar(demo_frame, orient=tk.HORIZONTAL, length=200, mode='indeterminate')
progress.pack(pady=10)
progress.start(10)
# 输入框
entry = ttk.Entry(demo_frame)
entry.pack(pady=5)
entry.insert(0, "输入文本...")
# 组合框
combo = ttk.Combobox(demo_frame, values=["选项1", "选项2", "选项3"])
combo.pack(pady=5)
combo.set("选择选项...")
# 复选框
check_var = tk.BooleanVar()
ttk.Checkbutton(demo_frame, text="选择我", variable=check_var).pack(pady=5)
def change_theme(self):
theme = self.current_theme.get()
self.style.theme_use(theme)
print(f"切换到主题: {theme}")
if __name__ == "__main__":
app = ThemedApp()
app.root.mainloop()
这个详细的 Tkinter 教程涵盖了从基础到高级的所有主要概念,包括:
- 基础组件:Label、Button、Entry、Text 等
- 布局管理:pack、grid、place
- 高级组件:Combobox、Listbox、Progressbar 等
- 菜单和对话框:完整的菜单系统、各种对话框
- 综合实例:功能完整的文本编辑器
- 最佳实践:面向对象编程、主题和样式
通过这些示例,你可以快速掌握 Tkinter 并创建功能丰富的桌面应用程序。