如何使用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 提供了三种布局管理器:packgridplace

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()

四.扩展

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

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

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

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

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功能概述

FigureCanvasTkAggmatplotlibtkinter 结合使用时的一个关键类。主要负责将 matplotlib 图形渲染到 tkinterCanvas 上。具体来说,它将一个 matplotlibFigure 对象嵌入到 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功能概述

FuncAnimationmatplotlib.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绑定figcanvas,而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()