如何使用python制作一个简单GUI界面

作者:柏子谦

一. 安装与导入 tkinter

tkinter 是 Python 的标准库之一,不需要单独安装。只需要在代码中导入即可:

import tkinter as tk
from tkinter import messagebox

二. 常用的基本部件

1. 创建一个基本窗口

使用 tkinter 创建一个基本窗口非常简单:

# 创建主窗口
root = tk.Tk()

# 设置窗口标题
root.title("我的第一个 GUI 程序")

# 设置窗口大小
root.geometry("400x300")

# 进入主事件循环
root.mainloop()

2. 添加标签(Label)

可以使用 Label 小部件在窗口中显示文本:

label = tk.Label(root, text="Hello, Tkinter!")
label.pack()

3. 添加按钮(Button)

可以使用 Button 小部件创建按钮,并指定按钮被点击时调用的函数:

def on_button_click():
    messagebox.showinfo("信息", "按钮被点击了!")

button = tk.Button(root, text="点击我", command=on_button_click)
button.pack()

4. 添加输入框(Entry)

可以使用 Entry 小部件创建文本输入框:

entry = tk.Entry(root)
entry.pack()

def on_submit():
    user_input = entry.get()
    messagebox.showinfo("信息", f"你输入了:{user_input}")

submit_button = tk.Button(root, text="提交", command=on_submit)
submit_button.pack()

5. 添加复选框(Checkbutton)

可以使用 Checkbutton 小部件创建复选框:

check_var = tk.IntVar()

checkbutton = tk.Checkbutton(root, text="同意条款", variable=check_var)
checkbutton.pack()

def on_check():
    if check_var.get():
        messagebox.showinfo("信息", "已同意条款")
    else:
        messagebox.showinfo("信息", "未同意条款")

check_button = tk.Button(root, text="检查复选框状态", command=on_check)
check_button.pack()

6. 添加单选按钮(Radiobutton)

可以使用 Radiobutton 小部件创建单选按钮:

radio_var = tk.StringVar()

radiobutton1 = tk.Radiobutton(root, text="选项1", variable=radio_var, value="1")
radiobutton2 = tk.Radiobutton(root, text="选项2", variable=radio_var, value="2")
radiobutton1.pack()
radiobutton2.pack()

def on_radio_select():
    selected_option = radio_var.get()
    messagebox.showinfo("信息", f"你选择了:{selected_option}")

select_button = tk.Button(root, text="检查单选按钮状态", command=on_radio_select)
select_button.pack()

三. 布局管理

tkinter 提供了三种布局管理器:pack、grid 和 place。

pack

pack 是最简单的布局管理器,它根据小部件的顺序将其添加到窗口中:

label1 = tk.Label(root, text="上")
label1.pack(side=tk.TOP)

label2 = tk.Label(root, text="左")
label2.pack(side=tk.LEFT)

label3 = tk.Label(root, text="右")
label3.pack(side=tk.RIGHT)

label4 = tk.Label(root, text="下")
label4.pack(side=tk.BOTTOM)

grid

grid 是一个基于网格的布局管理器,可以更灵活地控制小部件的位置:

label1 = tk.Label(root, text="行0 列0")
label1.grid(row=0, column=0)

label2 = tk.Label(root, text="行0 列1")
label2.grid(row=0, column=1)

label3 = tk.Label(root, text="行1 列0")
label3.grid(row=1, column=0)

label4 = tk.Label(root, text="行1 列1")
label4.grid(row=1, column=1)

place

place 是一个绝对布局管理器,可以精确控制小部件的位置:

label1 = tk.Label(root, text="绝对位置")
label1.place(x=100, y=50)

完整示例

import tkinter as tk
from tkinter import messagebox

def on_button_click():
    messagebox.showinfo("信息", "按钮被点击了!")

def on_submit():
    user_input = entry.get()
    messagebox.showinfo("信息", f"你输入了:{user_input}")

def on_check():
    if check_var.get():
        messagebox.showinfo("信息", "已同意条款")
    else:
        messagebox.showinfo("信息", "未同意条款")

def on_radio_select():
    selected_option = radio_var.get()
    messagebox.showinfo("信息", f"你选择了:{selected_option}")

# 创建主窗口
root = tk.Tk()
root.title("我的第一个 GUI 程序")
root.geometry("400x300")

# 标签
label = tk.Label(root, text="Hello, Tkinter!")
label.pack()

# 按钮
button = tk.Button(root, text="点击我", command=on_button_click)
button.pack()

# 输入框
entry = tk.Entry(root)
entry.pack()

submit_button = tk.Button(root, text="提交", command=on_submit)
submit_button.pack()

# 复选框
check_var = tk.IntVar()
checkbutton = tk.Checkbutton(root, text="同意条款", variable=check_var)
checkbutton.pack()

check_button = tk.Button(root, text="检查复选框状态", command=on_check)
check_button.pack()

# 单选按钮
radio_var = tk.StringVar()
radiobutton1 = tk.Radiobutton(root, text="选项1", variable=radio_var, value="1")
radiobutton2 = tk.Radiobutton(root, text="选项2", variable=radio_var, value="2")
radiobutton1.pack()
radiobutton2.pack()

select_button = tk.Button(root, text="检查单选按钮状态", command=on_radio_select)
select_button.pack()

# 进入主事件循环
root.mainloop()

四.扩展

首先要补充tkinter 中 Canvas 这个非常强大的小部件:

Canvas 是一个绘图区域,可以在其中绘制线条、矩形、圆形、文本和图像等。Canvas 小部件非常适合创建需要自定义绘图的应用程序,如图表、游戏、图形编辑器等。

它既有一些内部函数可以直接调用,如绘制线条canvas.create_line、绘制矩形canvas.create_rectangle、绘制椭圆canvas.create_oval、绘制多边形canvas.create_polygon、绘制文本canvas.create_text。也可以进行一些简单的事件处理,下面的示例代码中绑定了鼠标左键点击事件,并在点击的位置显示坐标。

进一步可以借助 PIL(Python Imaging Library)库来加载并显示图像。需要安装 PIL 或 Pillow 库:

pip install pillow

上述基本功能的简单示例:

import tkinter as tk
from PIL import Image, ImageTk

def on_canvas_click(event):
    x, y = event.x, event.y
    canvas.create_text(x, y, text=f"({x}, {y})", font=("Arial", 12), fill="black")

root = tk.Tk()
root.title("Canvas 示例")
root.geometry("500x400")

canvas = tk.Canvas(root, width=500, height=400, bg="white")
canvas.pack()

# 绘制基本形状
canvas.create_line(50, 50, 200, 50, fill="blue", width=5)
canvas.create_rectangle(50, 100, 200, 200, outline="red", width=3)
canvas.create_oval(250, 100, 400, 200, outline="green", width=3)
canvas.create_polygon(250, 250, 300, 300, 350, 250, fill="yellow", outline="black")
canvas.create_text(250, 50, text="Hello, Canvas!", font=("Arial", 20), fill="purple")

# 加载并显示图像
image = Image.open("path/to/your/image.jpg")
photo = ImageTk.PhotoImage(image)
canvas.create_image(250, 200, image=photo)

# 绑定事件
canvas.bind("<Button-1>", on_canvas_click)

root.mainloop()

1.实现简单的实时动画

借助 matplotlib库来实现简单的实时动画。需要安装 matplotlib 库:

pip install matplotlib

导入如下:

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt

FigureCanvasTkAgg功能概述

FigureCanvasTkAgg 是 matplotlib 和 tkinter 结合使用时的一个关键类。主要负责将 matplotlib 图形渲染到 tkinter 的 Canvas 上。具体来说,它将一个 matplotlib 的 Figure 对象嵌入到 tkinter 窗口中,使得可以在 tkinter 的应用程序中展示 matplotlib 图形,并且可以对图形进行交互(例如缩放、平移等)。

使用示例

以下是一个完整的示例,演示如何在 tkinter 窗口中嵌入 matplotlib 图形:

import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# 创建主窗口
root = tk.Tk()
root.title("嵌入 Matplotlib 图形")
root.geometry("800x600")

# 创建一个 Matplotlib 图形对象
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(111)
ax.plot([0, 1, 2, 3, 4], [10, 1, 20, 15, 10])

# 将 Matplotlib 图形嵌入到 Tkinter 窗口中
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

# 添加退出按钮
def on_exit():
    root.quit()
    root.destroy()

button = ttk.Button(master=root, text="退出", command=on_exit)
button.pack(side=tk.BOTTOM)

# 启动主事件循环
root.mainloop()

FuncAnimation功能概述

FuncAnimation 是 matplotlib.animation 模块中的一个类,用于创建动画。它可以通过不断调用一个更新函数来生成动画效果,适用于在 matplotlib 图表上动态展示数据变化。

简单使用示例
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# 创建一个图形对象和子图
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'b-', animated=True)

# 初始化函数,用于设置图表的初始状态
def init():
    ax.set_xlim(0, 2 * np.pi)
    ax.set_ylim(-1, 1)
    return ln,

# 更新函数,用于更新图表上的数据
def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

# 创建动画
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2 * np.pi, 128),
                    init_func=init, blit=True)

# 显示动画
plt.show()

综上,通过FigureCanvasTkAgg绑定fig与canvas,而fig任你发挥,无论是上述的利用FuncAnimation建立实时动画,还是利用matplotlib实现其他更多样的功能。

2.实现简单的摄像头交互

除了上述的PIL库,还用到了博大精深的opencv,因为作者时间紧张,这里仅提供一个作者之前测试基本功能的简单demo,有着完善的备注作为参考,有兴趣的同学可以copy运行。

import os
import cv2
from tkinter import *
from PIL import Image, ImageTk
import time

# 创建一个新的文件夹
output_folder = "output_videos"
os.makedirs(output_folder, exist_ok=True)

# 指定视频文件路径
video_file_path = os.path.join(output_folder, "output.avi")

# 创建TKinter窗口或框架的实例
win = Tk()

# 设置窗口的大小
win.geometry("640x480")

# 创建标签以捕获视频帧
label = Label(win)
label.grid(row=0, column=0)

# 打开默认摄像头
cap = cv2.VideoCapture(1, cv2.CAP_DSHOW)
cap.isOpened()

# 设置摄像头的分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_FPS, 30)

# 定义视频编解码器并创建 VideoWriter 对象
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
out = cv2.VideoWriter(video_file_path, fourcc, 30.0, (640, 480))

i = 0
fps = "0"
t1 = time.time()
# 显示帧
def show_frame():
    global i
    global t1
    global fps
    ret, frame = cap.read()
    i += 1
    if (time.time() - t1) > 1:
        fps = i / (time.time() - t1)
        fps = str(round(fps,2))
        i = 0
        t1 = time.time()
    cv2.putText(frame, "FPS:"+fps, (20, 20), 1, 1.5, (255, 255, 255), 2)
    if ret:
        # 将帧写入视频文件
        out.write(frame)
        
        # 将帧从 BGR 转换为 RGB
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        
        # 将帧转换为 ImageTk 格式
        frame = ImageTk.PhotoImage(Image.fromarray(frame))
        
        # 更新标签的图像
        label.imgtk = frame
        label.configure(image=frame)
        
        # 20 毫秒后显示下一帧
        label.after(20, show_frame)

# 关闭窗口时释放资源
def on_closing():
    out.release()
    cap.release()
    cv2.destroyAllWindows()
    win.destroy()

win.protocol("WM_DELETE_WINDOW", on_closing)

show_frame()
win.mainloop()