
简介这是一套基于Python与MySQL数据库的学生管理系统界面版资源主要面向正在学习Tkinter窗口编程和数据库增删改查的入门开发者。系统通过Python DB-API连接MySQL服务器借助Tkinter库构建包含按钮、文本框等组件的图形界面完整实现学生信息的添加、删除、修改和查询此外还结合了虚拟环境配置方便直接运行。整个资源包共754个文件包括452个py源码文件用于阶梯式研读140余个pyc编译文件以及58个pyd扩展文件提升模块加载效率13个exe可执行程序让大家无需配置即可体验功能同时还附带dll、cfg等运行依赖与配置文件整体打包约11.97MB体积小、下载快非常适合课程设计或毕业设计参考。目前已有2498人参与学习读者拿到手不仅有完整源码还有现成的可执行程序和依赖环境能够快速启动项目并根据自身需求二次开发一套更完善的学生管理系统。1. 从“Python界面版学生系统”开始带界面的学生管理系统该做什么事把控制台里的学生管理改成界面版工作量往往超出预期。控制台程序是顺序执行的输入命令、调用函数、输出结果而界面版要处理的是事件驱动用户可能随时点“新增”、点“删除”、关窗口甚至把成绩留空就提交。这时候状态维护、表单校验、表格刷新和数据库连接释放都成了必须解决的问题。这也是为什么“Python学生管理系统界面版”看起来简单却是很适合做课程设计和小型项目练手的原因一个项目同时覆盖 Python 基础语法、SQLite 数据操作和 GUI 事件模型。如果你已经写了不少脚本但还没系统接触过 tkinter 或 PySide6这篇文章可以从建表一直带到打包发布。2. 先分层再写代码SQLite 数据模型与 tkinter / PySide6 选型界面版最忌讳的是把增删改查全部塞进按钮的回调函数里。很多学生系统源码看起来能用但改一个字段要动三个位置就是因为界面逻辑和业务逻辑混在一起。我的做法是先定好数据模型和业务层再开始写窗口这样后面换 GUI 框架也不需要重写数据访问代码。2.1 选 GUI 框架tkinter 和 PySide6 在界面版学生系统上的取舍做学生系统界面版常见选择是 tkinter 或 PySide6。tkinter 是 Python 自带的标准库不需要额外安装在 Windows、macOS 和常用 Linux 桌面环境里都能直接运行。PySide6 是 Qt 的官方 Python 绑定界面更现代表格控件更强大但要先安装再使用对初学者来说多了环境这一步。框架依赖表格控件适合场景tkinterPython 自带Linux 下可能需安装 python3-tkttk.Treeview小型管理类工具、课程设计、快速原型PySide6pip install PySide6QTableView QSqlTableModel更复杂的数据展示、需要高级样式和报表如果你的需求只是增删改查加一个统计图tkinter 足够。如果要做那种带筛选、排序、分页而且数据量可能上万的学生系统我会直接选 PySide6因为它的 Model/View 架构可以把数据库表和界面表格绑定起来减少手动刷新的代码。不要一开始就追求框架新旧学生系统的核心复杂度在数据一致性而不在界面特效。2.2 数据层字段与 SQLite 建表学生系统最常用的数据库是 SQLite不需要单独安装服务一个文件就是一个库。字段方面除了基本的学生信息我建议把“班级”和“成绩”单独作为字段而不是塞进备注。下面是一份通用建表语句CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_no TEXT UNIQUE NOT NULL, name TEXT NOT NULL, gender TEXT DEFAULT 男, class_name TEXT, score REAL, created_at TEXT DEFAULT (datetime(now, localtime)) );参数说明student_no用UNIQUE约束避免同一个学号被录入两次score用REAL类型而不是 varchar否则排序和平均值计算都会变成字符串操作结果会非常奇怪created_at让数据库自动写入本地时间界面不用自己拼日期。这张表足够支撑一个界面版学生系统的大部分操作。2.3 数据库封装与业务层窗口里不应该直接写sqlite3.connect和大段 SQL。我一般会把数据库操作封装成一个独立模块比如db.py对外只暴露函数。好处是界面层只关心“调用什么函数”不关心“怎么连接数据库”。import sqlite3 from contextlib import contextmanager DB_FILE students.db def get_conn(): conn sqlite3.connect(DB_FILE) conn.row_factory sqlite3.Row return conn contextmanager def open_cursor(): conn get_conn() try: cur conn.cursor() yield cur conn.commit() finally: conn.close() def add_student(student_no, name, gender, class_name, score): with open_cursor() as cur: cur.execute( INSERT INTO students (student_no, name, gender, class_name, score) VALUES (?, ?, ?, ?, ?), (student_no, name, gender, class_name, score), ) def list_students(): with open_cursor() as cur: cur.execute(SELECT * FROM students ORDER BY id DESC) return [dict(row) for row in cur.fetchall()]这里的?是 SQLite 的参数化占位符不是字符串拼接能有效防止 SQL 注入。open_cursor用 contextmanager 保证无论执行成功还是报错连接都会关闭。dict(row)把查询结果转换成字典界面层用row[name]取值比row[1]可读性强很多。要注意的是这个写法对每个操作都会新建和关闭连接学生系统这种轻量场景没问题但如果要做高并发访问就应该改成应用启动时建立一个长连接这里不展开。3. 用 tkinter 实现学生系统增删改查主窗口、表格刷新与 python 环境排查现在进入界面部分。以下代码基于 tkinter ttk 实现一个能跑通的最小版本。整个窗口由一个 Treeview 表格和几个按钮组成数据来源于db.py。3.1 最小可运行Treeview 加载数据先写一个最简窗口类初始化表格列然后从数据库读取数据填充。这个版本不需要额外安装任何第三方库只要 Python 环境正常就能运行。import tkinter as tk from tkinter import ttk from db import list_students class App(tk.Tk): def __init__(self): super().__init__() self.title(Python学生管理系统界面版) self.geometry(720x400) columns (id, student_no, name, gender, class_name, score, created_at) self.tree ttk.Treeview(self, columnscolumns, showheadings) for col in columns: self.tree.heading(col, textcol) self.tree.column(col, width90, anchorcenter) self.tree.pack(fillboth, expandTrue) self.refresh() def refresh(self): for item in self.tree.get_children(): self.tree.delete(item) for row in list_students(): self.tree.insert(, end, values( row[id], row[student_no], row[name], row[gender], row[class_name], row[score], row[created_at] )) if __name__ __main__: app App() app.mainloop()参数说明showheadings的意思是只显示列标题不显示最左侧的层级树状列否则表格左边会多一列空白。anchorcenter让列内容居中比默认左对齐更接近管理系统的展示习惯。refresh先删除所有旧行再重新插入这是界面版最简单的刷新方式虽然没有做增量更新但在几百条数据下性能完全够用。3.2 新增与修改用 Toplevel 弹窗收集表单数据主窗口上放“新增学生”按钮后最直接的做法是用tk.Toplevel弹出一个子窗口。下面代码演示新增弹窗字段包含学号、姓名、性别、班级和成绩。from tkinter import messagebox from db import add_student def open_add_dialog(self): win tk.Toplevel(self) win.title(新增学生) win.grab_set() entries {} fields [(student_no, 学号), (name, 姓名), (gender, 性别), (class_name, 班级), (score, 成绩)] for i, (key, label) in enumerate(fields): tk.Label(win, textlabel).grid(rowi, column0, padx8, pady6) entry tk.Entry(win) entry.grid(rowi, column1, padx8, pady6) entries[key] entry def submit(): data {key: entry.get().strip() for key, entry in entries.items()} if not data[student_no] or not data[name]: messagebox.showwarning(校验失败, 学号和姓名不能为空) return try: score float(data[score]) if data[score] else None except ValueError: messagebox.showerror(格式错误, 成绩必须是数字) return add_student(data[student_no], data[name], data[gender], data[class_name], score) win.destroy() self.refresh() tk.Button(win, text保存, commandsubmit).grid( rowlen(fields), column0, columnspan2, pady10 )这段代码的核心点在grab_set()它会把输入焦点锁定在弹窗上用户必须先处理弹窗才能回到主窗口避免同一时间打开多个新增窗口。entry.get().strip()去掉首尾空格防止用户输入带空格的学号。成绩允许为空但非空时必须能被float转换这里就是 Python 类型转换最常见的应用场景。修改学生的逻辑与新增类似区别在于打开弹窗时要先把当前选中行的值填入 Entry。具体可以从self.tree.selection()拿到选中项的 iid然后用self.tree.item(iid, values)读取原始数据。3.3 刷新与排序的坑不要直接修改 Treeview 的 iid第一次写界面版学生系统时很容易踩一个坑在refresh里用self.tree.insert(, end, values...)插入数据然后用self.tree.selection()拿到的选中项去操作数据库。看起来没问题但如果删除一条记录后没有正确刷新Treeview 的 item 和数据库行的对应关系就会错乱。我的建议是插入时显式指定iid为数据库主键self.tree.insert(, end, iidstr(row[id]), values(...))这样self.tree.selection()返回的第一个值就是行 ID删除时直接把这个字符串转成整数传给delete_student(int(iid))。如果不用iid想定位一行只能遍历 Treeview 的children代码又多又慢。排序也一样不要在 Python 里对 list 排序直接改 SQLSELECT * FROM students ORDER BY class_name ASC, score DESC数据库索引和排序比界面层自己做稳定得多而且当数据量上来时不容易写错。3.4 运行前环境python 安装、vscode 配置与国内源运行 tkinter 程序时最常见的报错是在终端输入python后弹出了 Microsoft Store 提示。这通常不是 Python 没安装而是没有把 Python 加入 PATH或者没有在编辑器里正确选择解释器。排查顺序是python --version which python python -c import tkinter; print(tkinter.TkVersion)第一行确认版本第二行查看实际执行的是哪个解释器第三行确认 tkinter 可用。在 Windows 上如果第三行报ModuleNotFoundError大概率是当时安装 Python 时没有勾选 tcl/tk 相关组件。在 Linux 系统上则需要单独安装 python3-tkUbuntu 下的命令是sudo apt install python3-tk。在 vscode 里配置 Python 环境不要依赖系统默认解释器。按CtrlShiftP执行 “Python: Select Interpreter”选择虚拟环境里的 Python这样终端和调试器用的解释器才一致。需要安装第三方依赖时可以使用国内镜像源加快速度pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple如果不想每次输入完整地址可以设置一次全局源。tkinter 本身不需要 pip 安装但后面做图表时会用到 matplotlib这个命令可以提前备着。4. 向“完整”靠拢学生系统输入校验、成绩统计、备份和 PyInstaller 打包一个界面版学生系统光有增删改查还不够至少要处理“用户乱输入”和“数据安全”这两件事。这一章把校验、统计、备份和打包串起来讲让项目可以交付出去。4.1 把字符串转成数字表单校验的三层防线Entry 返回的一定是字符串如果直接把用户输入的“18.5分”存进 score 字段后期统计平均值时会直接报错。我会设置三层防线。第一层是界面层校验用try/except捕获转换异常def parse_score(raw): if raw in (, None): return None try: val float(raw) except (TypeError, ValueError): raise ValueError(f成绩无法解析: {raw!r}) if not (0 val 100): raise ValueError(成绩应在 0~100 之间) return val第二层是业务层所有入口都调用parse_score而不是在多个按钮回调里重复写float(...)。第三层是数据库约束CREATE TABLE IF NOT EXISTS students ( ... score REAL CHECK (score 0 AND score 100) );这样即使界面层漏检数据库也不会接受超出范围的数据。三类校验各管一段任何一层崩溃都能被上层接住。4.2 用 matplotlib 嵌入主窗口做班级成绩统计学生系统的加分项通常是统计功能。先用 SQL 按班级聚合成绩再画柱状图这和“python数据分析与可视化”里的思路一致。SELECT class_name, AVG(score) AS avg_score, COUNT(*) AS cnt FROM students WHERE score IS NOT NULL GROUP BY class_name ORDER BY avg_score DESC画图部分可以用 matplotlib 嵌入 tkinter而不是单独弹一个窗口。做法是在主界面的右侧放一个FigureCanvasTkAggimport matplotlib matplotlib.use(TkAgg) from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure class StatsPanel(ttk.Frame): def __init__(self, master): super().__init__(master) self.fig Figure(figsize(4, 3), dpi100) self.ax self.fig.add_subplot(111) self.canvas FigureCanvasTkAgg(self.fig, masterself) self.canvas.get_tk_widget().pack(fillboth, expandTrue) def draw(self, class_names, avg_scores): self.ax.clear() self.ax.bar(class_names, avg_scores) self.ax.set_ylabel(平均分) self.canvas.draw()注意matplotlib.use(TkAgg)这句必须在使用 tkinter 创建窗口之前执行否则画布可能无法嵌入。如果你的数据有更多维度比如想按性别、班级做层次聚类分析也可以在这个数据结构上继续扩展但学生系统一般画到平均分对比就够了。4.3 参数化查询与数据库备份删除和更新操作同样要用参数化 SQLdef delete_student(student_id): with open_cursor() as cur: cur.execute(DELETE FROM students WHERE id ?, (student_id,))不要把 student_id 直接拼进 SQL 字符串。虽然学生系统看起来没有安全风险但一旦用户输入内容里带单引号拼接写法很容易引发 SQL 语法错误甚至注入。参数化是习惯问题不是看系统大小决定要不要用。备份数据库最简单的方式是借助 SQLite 的在线备份接口import sqlite3 source sqlite3.connect(students.db) backup sqlite3.connect(students_backup.db) source.backup(backup) backup.close() source.close()在程序退出前自动备份可以避免用户误删数据后无法恢复。这个备份操作会复制完整数据库文件不需要停止应用。4.4 用 PyInstaller 打包成 exe交付给没有 Python 环境的机器时用 PyInstaller 打包是常见做法。基本命令是pip install pyinstaller pyinstaller -F -w main.py --name 学生管理系统-F表示打包成单个文件-w表示不显示控制台窗口--name指定输出文件名。第一次打包建议在干净环境里执行不要在自己装了无数依赖的全局环境里打包否则最终 exe 体积会比实际需要大很多。打包后要注意students.db文件会生成在程序运行目录而不是打包后的临时解压目录。不要试图把数据库文件放在 PyInstaller 的内部路径里用户运行目录不可控后面要改数据会很麻烦。最简单的做法是首次启动时检查当前目录下是否存在students.db不存在就自动建库。5. 验证与打磨界面版学生系统的回归清单和三个界面细节5.1 一个完整的回归测试清单交付前不要只点一遍“新增”就算测完。按照下面这个顺序回归能覆盖 80% 的常见问题启动程序后窗口是否能正常显示新增一条完整学生记录表格是否立刻刷新学号留空时点击保存是否被拦截成绩输入“abc”时是否弹出错误提示而不是崩溃成绩输入空值时是否能正常保存修改记录后关闭程序重新打开数据是否还在删除一条选中记录是否要求二次确认快速连续双击多个按钮是否出现重复窗口或异常数据打包后的 exe 在另一台没有 Python 的机器上是否能启动。其中“快速连续双击”最容易暴露问题因为 tkinter 默认不处理按钮防抖双击一个按钮可能触发两次新增。如果希望严谨一点可以在 submit 开始时先禁用保存按钮保存完成后再恢复。5.2 三个值得做的界面细节第一个是使用 ttk 主题统一控件风格style ttk.Style() style.theme_use(clam)clam主题在不同系统上的兼容性比默认主题好表格的选色和按钮边距也更可控。第二个是给按钮绑定快捷键。例如删除按钮绑定到Delete键保存弹窗绑定到CtrlEnter。在 tkinter 中可以用bind_all但要注意在主窗口绑定Delete时焦点如果在 Entry 输入框里会先触发输入框的删除行为。我一般会判断事件来源控件def on_delete_key(event): if not isinstance(event.widget, ttk.Treeview): return self.delete_selected()第三个是退出时确保数据库连接和备份完成。在App类里重写destroy()def destroy(self): try: self._backup() finally: super().destroy()这样用户无论点击关闭按钮还是调用quit都会先备份数据再退出避免写到一半被强制结束导致students.db损坏。本文还有配套的精品资源点击获取