Python青少年简明教程:tkinter库入门
tkinter是Python的标准GUI(图形用户界面)库。它提供了一种快速而简单的方法来创建GUI应用程序。tkinter是Python自带的,无需额外安装,随 Python 安装包一起提供。
在Python 3.x中,库名称是tkinter, 跨平台——在 Windows、macOS 和 Linux 上都可以使用,使得用 Tkinter 编写的程序可以在不同操作系统上运行。
tkinter基于事件驱动编程模型,它提供了一组小部件(如按钮、标签、文本框等)和几何管理器(如pack、grid、place)来组织用户界面,适合构建简单的桌面应用程序。
基本概念
窗口(Window):Tkinter应用程序的基本组件之一,表示一个包含菜单栏、工具栏、状态栏以及主体区域的窗口。窗口可以通过Tk()或Toplevel()方法创建,其中Tk()方法创建的是应用程序的主窗口,而Toplevel()方法创建的是一个独立的子窗口。
tkinter的窗口可以通过Tk()或Toplevel()方法创建,两者区别:
Tk() 用于创建应用程序的主窗口——用于创建应用程序的主界面。这是应用程序的根窗口。通常每个应用程序只有一个Tk()实例。关闭Tk()窗口会结束整个应用程序,所有Toplevel窗口也会销毁。Tk()包含主事件循环(mainloop())。
Toplevel()用于创建子窗口或额外的窗口——常用于创建对话框、额外的信息窗口或功能窗口。通常需要一个父窗口作为参数(通常是Tk()实例或另一个Toplevel()实例)。可以创建多个Toplevel()窗口。关闭Toplevel()窗口只会关闭该特定窗口,不会影响主窗口或其他Toplevel窗口。Toplevel()不包含自己的事件循环,依赖于Tk()的主事件循环。
示例:
# Tkinter中的Tk()和Toplevel()创建的窗口区别演示
import tkinter as tk
# 使用Tk()创建主窗口
root = tk.Tk()
root.title("主窗口 (Tk)")
root.geometry("300x200")
# 创建一个按钮来打开Toplevel窗口
def open_toplevel():
# 使用Toplevel()创建子窗口
top = tk.Toplevel(root)
top.title("子窗口 (Toplevel)")
top.geometry("250x150")
tk.Label(top, text="这是一个Toplevel窗口").pack(pady=20)
tk.Button(root, text="打开Toplevel窗口", command=open_toplevel).pack(pady=50)
tk.Label(root, text="这是主Tk窗口").pack()
root.mainloop()
对 Tkinter 的支持分布在多个模块中。 大多数应用程序将需要主模块 tkinter(也称为tk)和tkinter.ttk,也成为 ttk(themed tk),后者从tkinter 8.5开始可用。ttk组件多于tkinter,界面也相对漂亮,所以使用时尽量选择ttk。按照以下方式导入,ttk中的widget(组件、小部件、控件)会默认替换掉tk的。
import tkinter as tk
from tkinter import ttk
大多数应用程序都需要同时使用这两个模块。一些常见的小部件在 tk 和 ttk 中都有实现,但 ttk 版本通常更为现代。
ttk(themed tk)是对基本 tkinter 的增强:
提供了更多的组件;
界面相对更现代和美观;
支持主题,使应用程序在不同平台上具有一致的外观。
特别提示,tkinte文档中使用的widgets,在许多不同的翻译:组件、小部件、控件,一般译为小部件,相当于某些开发语言的Components(组件)、controls(控件)。这些术语可以互换使用,但在 tkinter 上下文中,“小部件”或“控件”可能更为常见。
ttk 和 tkinter (tk) 共有的和各自独有的小部件 (widgets)
一些常见的小部件在 tk 和 ttk 中都有实现,但 ttk 版本通常更为现代。
ttk 和 tkinter 都提供了以下基本小部件:
Button(按钮)
Label(标签)
Entry(输入框)
Frame(框架)
Checkbutton(复选框)
Radiobutton(单选按钮)
Scale(滑块)
Scrollbar(滚动条)
tkinter (tk) 独有的小部件:
Menu(菜单)
Canvas(画布)
Text(文本框)
Message(消息框):用于显示多行文本
Spinbox(数值调节框):允许用户从一系列值中选择
ttk 独有的小部件:
Combobox(组合框):结合了 Entry 和下拉列表
Notebook(笔记本):用于创建选项卡式界面
Progressbar(进度条):显示任务进度
Separator(分隔符):用于分隔其他小部件
Sizegrip(大小调整手柄):允许用户调整窗口大小
Treeview(树状视图):用于显示层次化数据
事件循环
tkinter 应用程序通常运行在一个事件循环中,等待用户交互并响应事件。tkinter 允许你通过绑定事件来响应用户交互。
tkinter 提供了几种布局管理器来控制 widgets 的位置和大小:
pack:按照上、下、左、右方向逐步排列widgets。
grid:使用网格系统布局 widgets。
place:精确控制 widgets 的位置和大小。
tkinter程序的基本结构
一个最简单的 tkinter 程序确实应该包含以下四个主要部分:
导入 tkinter 模块
创建主窗口(root 窗口)
添加人机交互控件和相应的事件函数
启动主循环(mainloop)
以下是一个包含多个 widgets 和事件处理的简单较完整的示例:
import tkinter as tk
from tkinter import messagebox
# 事件处理函数
def on_button_click():
messagebox.showinfo("Info", "Button clicked!")
# 创建主窗口
root = tk.Tk()
# 设置窗口大小为宽300像素,高200像素
root.geometry("300x200")
# 设置窗口标题
root.title("Tkinter Example")
# 创建标签
label = tk.Label(root, text="Hello, Tkinter!")
label.pack()
# 创建按钮,绑定事件处理函数
button = tk.Button(root, text="Click Me", command=on_button_click)
button.pack()
# 进入主事件循环
root.mainloop()
上面示例运行效果:
geometry方法用于设置窗口的大小和位置。它允许开发者定义窗口的宽度、高度以及在屏幕上的位置。以像素为单位。
geometry 方法的常规格式如下:
窗口.geometry("宽度x高度+偏移X+偏移Y")
其中 +偏移x+偏移y 部分是可选的。
注意事项
如果仅指定窗口大小,例如 窗口.geometry("800x600"),窗口将以默认位置显示。
如果仅指定位置,例如 窗口.geometry("+100+100"),窗口的大小将保持默认。
如果设置的大小超出了屏幕的可视范围,窗口可能会部分不可见。
对话框
tkinter提供了多种预定义的对话框子模块:
messagebox:显示消息或警告
filedialog:文件选择对话框
colorchooser:颜色选择对话框
messagebox、filedialog和colorchooser都tkinter 的子模块
messagebox 是 tkinter 的一个子模块,用于创建消息对话框。
导入方式:from tkinter import messagebox
主要功能:显示信息、警告或错误消息,以及获取简单的用户输入(如是/否选择)。
常用函数:showinfo(), showwarning(), showerror(), askyesno() 等。
例如:
from tkinter import messagebox
messagebox.showinfo("信息", "这是一条信息")
result = messagebox.askyesno("确认", "您确定要继续吗?")
filedialog 是 tkinter 的另一个子模块,用于创建文件选择对话框。
导入方式:from tkinter import filedialog
主要功能:允许用户选择文件或目录。
常用函数:askopenfilename(), asksaveasfilename(), askdirectory() 等。
例如:
from tkinter import filedialog
filename = filedialog.askopenfilename(title="选择文件", filetypes=(("文本文件", "*.txt"), ("所有文件", "*.*")))
colorchooser 是 tkinter 的子模块,用于创建颜色选择对话框。
导入方式:from tkinter import colorchooser
主要功能:允许用户选择颜色。
主要函数:askcolor()
例如:
from tkinter import colorchooser
color = colorchooser.askcolor(title="选择颜色")
以下是一个综合使用这些子模块的简单较完整的示例:
import tkinter as tk
from tkinter import messagebox, filedialog, colorchooser
def show_message():
messagebox.showinfo("信息", "这是一条信息")
def choose_file():
filename = filedialog.askopenfilename(title="选择文件", filetypes=(("文本文件", "*.txt"), ("所有文件", "*.*")))
if filename:
messagebox.showinfo("文件选择", f"您选择的文件是:{filename}")
def choose_color():
color = colorchooser.askcolor(title="选择颜色")
if color[1]: # color is None if dialog is cancelled
messagebox.showinfo("颜色选择", f"您选择的颜色是:{color[1]}")
root = tk.Tk()
root.geometry("300x200")
root.title("tkinter 对话框示例")
tk.Button(root, text="显示消息", command=show_message).pack(pady=5)
tk.Button(root, text="选择文件", command=choose_file).pack(pady=5)
tk.Button(root, text="选择颜色", command=choose_color).pack(pady=5)
root.mainloop()
运行效果:
Menu和MenuButton
Menu 和 MenuButton 都是 tkinter 提供的控件类。
Menu 是 tkinter 中用于创建菜单栏、下拉菜单和弹出菜单的类。
用于创建各种类型的菜单,包括顶级菜单栏、下拉菜单和上下文菜单。
可以包含命令、子菜单、复选框菜单项等。
通常用于创建应用程序的主菜单栏。
MenuButton 是 tkinter 中的一个控件类,用于创建一个带有相关联的下拉菜单的按钮。
是一个结合了按钮和菜单功能的控件。
点击 MenuButton 会显示一个下拉菜单。
常用于工具栏或需要节省空间的界面设计。
示例:
import tkinter as tk
def do_command():
print("Command executed!")
root = tk.Tk()
root.geometry("300x200")
root.title("Menu 和 MenuButton 示例")
# 创建顶级菜单栏
menubar = tk.Menu(root)
root.config(menu=menubar)
# 创建 "File" 菜单
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="New", command=do_command)
file_menu.add_command(label="Open", command=do_command)
file_menu.add_separator()
file_menu.add_command(label="Exit", command=root.quit)
# 创建 MenuButton
mb = tk.Menubutton(root, text="Options", relief=tk.RAISED)
mb.pack(padx=10, pady=10)
# 为 MenuButton 创建菜单
mb_menu = tk.Menu(mb, tearoff=0)
mb["menu"] = mb_menu
mb_menu.add_command(label="Option 1", command=do_command)
mb_menu.add_command(label="Option 2", command=do_command)
root.mainloop()
运行效果:
Canvas(画布)小部件
canvas 是 tkinter 中的一个小部件,因其功能强大,常被单独讨论。
它是 tkinter 的一部分,不需要单独导入。
提供了一个绘图区域,可以在其中创建线条、形状、文本和图像等。
常用于创建自定义图形、图表或动画。
以下是一些常用的 Canvas 方法:
create_line(x1, y1, x2, y2, options):绘制一条从 (x1, y1) 到 (x2, y2) 的直线。
create_rectangle(x1, y1, x2, y2, options):绘制一个矩形。
create_oval(x1, y1, x2, y2, options):绘制一个椭圆。
create_text(x, y, text, options):在指定位置显示文本。
create_image(x, y, image, options):在指定位置显示图像。
delete(what):删除指定的图形项。
move(tagOrId, x, y):移动指定的图形项。
bind(sequence, func):绑定事件处理程序。
示例:
import tkinter as tk
from tkinter import PhotoImage
# 创建主窗口
root = tk.Tk()
root.title("Tkinter Canvas Example")
# 创建 Canvas 组件
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()
# 绘制直线
canvas.create_line(10, 20, 200, 20, fill="blue", width=2)
# 绘制矩形
canvas.create_rectangle(50, 50, 150, 150, outline="black", fill="yellow", width=2)
# 绘制椭圆
canvas.create_oval(100, 100, 250, 200, outline="green", fill="red", width=2)
# 显示文本
canvas.create_text(200, 50, text="Hello Canvas", font=("Arial", 16))
# 加载图像并显示
try:
img = PhotoImage(file="example.png") # 确保你有一个名为 example.png 的图像文件
canvas.create_image(300, 300, image=img, anchor="center")
except tk.TclError:
print("Image file not found or cannot be loaded.")
# 定义事件处理程序
def on_click(event):
print(f"Canvas clicked at ({event.x}, {event.y})")
# 绑定点击事件
canvas.bind("<Button-1>", on_click)
# 进入主事件循环
root.mainloop()
运行效果:
ttk(主题Tkinter小部件)
ttk(Themed Tkinter Widgets)是 tkinter 的一个扩展,提供了访问 Tk 主题小部件集的接口。ttk 提供了一组主题化的小部件,相比 Tkinter 的传统小部件,ttk 小部件具有更多的功能和更好的外观。
完整导入路径:from tkinter import ttk
提供了与 tkinter 相似但外观更现代的小部件。
ttk 小部件可以使用主题,使得应用程序在不同平台上具有一致的外观。
常用的 ttk 控件
ttk 提供了一些与 Tkinter 传统控件对应的改进版本:
ttk.Button:改进版的按钮控件。
ttk.Label:改进版的标签控件。
ttk.Entry:改进版的单行文本输入控件。
ttk.Text:改进版的多行文本输入控件。
ttk.Checkbutton:改进版的复选框控件。
ttk.Radiobutton:改进版的单选按钮控件。
ttk.Combobox:下拉组合框控件。
ttk.Treeview:树状视图控件。
ttk.Notebook:选项卡控件。
以下是一个使用 ttk 控件的简单示例,展示了如何创建和使用 ttk 控件:
import tkinter as tk
from tkinter import ttk
# 创建主窗口
root = tk.Tk()
root.geometry("300x200")
root.title("TTK Widgets Example")
# 创建 ttk 样式
style = ttk.Style()
style.configure("TButton", padding=6, relief="flat", background="#ccc")
# 创建 ttk 按钮
button = ttk.Button(root, text="Click Me")
button.pack(pady=10)
# 创建 ttk 标签
label = ttk.Label(root, text="Hello, TTK!")
label.pack(pady=10)
# 创建 ttk 组合框
combo = ttk.Combobox(root, values=["Option 1", "Option 2", "Option 3"])
combo.pack(pady=10)
# 进入主事件循环
root.mainloop()
运行效果:
下面给使用tkinter库创建的小游戏:简单的弹球游戏,在画布上生成一个蓝色的小球随机运动,小球碰到边缘会反弹,底部有一红色的挡板,玩家可以使用左右方向键控制它,挡板接到小球时会反弹,玩家得分1分,小球落到球拍下方扣1分。源码如下:
import tkinter as tk
import random
# 初始化Tkinter窗口
window = tk.Tk()
window.title("弹球游戏")
# 创建画布
canvas_width = 800
canvas_height = 600
canvas = tk.Canvas(window, width=canvas_width, height=canvas_height, bg="white")
canvas.pack()
# 球体参数
ball_radius = 20
ball_x = random.randint(ball_radius, canvas_width - ball_radius)
ball_y = random.randint(ball_radius, canvas_height - ball_radius)
ball_dx = random.randint(2, 8)
ball_dy = random.randint(2, 8)
ball = canvas.create_oval(ball_x - ball_radius, ball_y - ball_radius,
ball_x + ball_radius, ball_y + ball_radius, fill="blue")
# 挡板参数
paddle_width = 100
paddle_height = 20
paddle_x = canvas_width // 2 - paddle_width // 2
paddle_y = canvas_height - paddle_height - 10
paddle = canvas.create_rectangle(paddle_x, paddle_y,
paddle_x + paddle_width, paddle_y + paddle_height, fill="red")
# 玩家得分
score = 0
# 移动球体的函数
def move_ball():
global ball_x, ball_y, ball_dx, ball_dy, score
# 更新球体位置
ball_x += ball_dx
ball_y += ball_dy
# 检测边缘碰撞
if ball_x <= ball_radius or ball_x >= canvas_width - ball_radius:
ball_dx *= -1
if ball_y <= ball_radius or ball_y >= canvas_height - ball_radius:
ball_dy *= -1
# 检测挡板碰撞
paddle_coords = canvas.coords(paddle)
if ball_y + ball_radius >= paddle_coords[1] and \
ball_x >= paddle_coords[0] and ball_x <= paddle_coords[2]:
ball_dy *= -1
score += 1
canvas.itemconfig(score_text, text=f"Score: {score}")
elif ball_y + ball_radius >= canvas_height:
score -= 1
canvas.itemconfig(score_text, text=f"Score: {score}")
# 更新球体位置
canvas.coords(ball, ball_x - ball_radius, ball_y - ball_radius,
ball_x + ball_radius, ball_y + ball_radius)
# 调用下一帧
window.after(30, move_ball)
# 控制挡板的函数
def move_paddle(event):
global paddle_x
if event.keysym == "Left" and paddle_x > 0:
paddle_x -= 10
elif event.keysym == "Right" and paddle_x < canvas_width - paddle_width:
paddle_x += 10
canvas.coords(paddle, paddle_x, paddle_y,
paddle_x + paddle_width, paddle_y + paddle_height)
# 创建分数显示
score_text = canvas.create_text(50, 20, anchor="nw", text="Score: 0")
# 绑定左右键事件
window.bind("<Left>", move_paddle)
window.bind("<Right>", move_paddle)
# 开始游戏循环
move_ball()
# 进入主循环
window.mainloop()
下面改用使用类(面向对象编程)实现,源码如下:
import tkinter as tk
import random
class BouncingBallGame:
def __init__(self, root):
self.root = root
self.root.title("弹球游戏")
# 创建画布
self.canvas_width = 800
self.canvas_height = 600
self.canvas = tk.Canvas(root, width=self.canvas_width, height=self.canvas_height, bg="white")
self.canvas.pack()
# 球体参数
self.ball_radius = 20
self.ball_x = random.randint(self.ball_radius, self.canvas_width - self.ball_radius)
self.ball_y = random.randint(self.ball_radius, self.canvas_height - self.ball_radius)
self.ball_dx = random.randint(2, 8)
self.ball_dy = random.randint(2, 8)
self.ball = self.canvas.create_oval(self.ball_x - self.ball_radius, self.ball_y - self.ball_radius,
self.ball_x + self.ball_radius, self.ball_y + self.ball_radius, fill="blue")
# 挡板参数
self.paddle_width = 100
self.paddle_height = 20
self.paddle_x = self.canvas_width // 2 - self.paddle_width // 2
self.paddle_y = self.canvas_height - self.paddle_height - 10
self.paddle = self.canvas.create_rectangle(self.paddle_x, self.paddle_y,
self.paddle_x + self.paddle_width, self.paddle_y + self.paddle_height, fill="red")
# 玩家得分
self.score = 0
self.score_text = self.canvas.create_text(50, 20, anchor="nw", text="Score: 0")
# 绑定左右键事件
self.root.bind("<Left>", self.move_paddle)
self.root.bind("<Right>", self.move_paddle)
# 启动游戏循环
self.move_ball()
def move_ball(self):
# 更新球体位置
self.ball_x += self.ball_dx
self.ball_y += self.ball_dy
# 检测边缘碰撞
if self.ball_x <= self.ball_radius or self.ball_x >= self.canvas_width - self.ball_radius:
self.ball_dx *= -1
if self.ball_y <= self.ball_radius or self.ball_y >= self.canvas_height - self.ball_radius:
self.ball_dy *= -1
# 检测挡板碰撞
paddle_coords = self.canvas.coords(self.paddle)
if self.ball_y + self.ball_radius >= paddle_coords[1] and \
self.ball_x >= paddle_coords[0] and self.ball_x <= paddle_coords[2]:
self.ball_dy *= -1
self.score += 1
self.canvas.itemconfig(self.score_text, text=f"Score: {self.score}")
elif self.ball_y + self.ball_radius >= self.canvas_height:
self.score -= 1
self.canvas.itemconfig(self.score_text, text=f"Score: {self.score}")
# 更新球体位置
self.canvas.coords(self.ball, self.ball_x - self.ball_radius, self.ball_y - self.ball_radius,
self.ball_x + self.ball_radius, self.ball_y + self.ball_radius)
# 调用下一帧
self.root.after(30, self.move_ball)
def move_paddle(self, event):
if event.keysym == "Left" and self.paddle_x > 0:
self.paddle_x -= 10
elif event.keysym == "Right" and self.paddle_x < self.canvas_width - self.paddle_width:
self.paddle_x += 10
self.canvas.coords(self.paddle, self.paddle_x, self.paddle_y,
self.paddle_x + self.paddle_width, self.paddle_y + self.paddle_height)
if __name__ == "__main__":
root = tk.Tk()
game = BouncingBallGame(root)
root.mainloop()
附录
Python 官方文档中的 Tkinter 参考 https://docs.python.org/zh-cn/3/library/tkinter.html
Python 的Tkinter包系列之一:窗口初步https://blog.csdn.net/cnds123/article/details/127227651
Python 的Tkinter包系列之二:菜单 https://blog.csdn.net/cnds123/article/details/127319885
Python 的Tkinter包系列之三:Canvas (画布)https://blog.csdn.net/cnds123/article/details/127344534
Python 的Tkinter包系列之四:对话框https://blog.csdn.net/cnds123/article/details/127392512
Python 的Tkinter包系列之五:事件https://blog.csdn.net/cnds123/article/details/127411016
Python 的Tkinter包系列之六:好例子https://blog.csdn.net/cnds123/article/details/127487982