简介PyQt5学生信息管理系统是一份面向Python初学者和毕业设计者的完整源码包基于PyQt5实现学生信息的增删改查整合pymysql、xlwt、MySQL及QtDesigner/pyuic工具链适合用作桌面端GUI开发入门练手项目。压缩包共34个文件包含8个Python脚本、7个Qt Designer生成的ui界面、对应pyc编译文件、4个xml配置、2个MySQL数据库sql脚本以及图片、图标、部署调试说明等整体仅257KB结构轻量清晰。已有466人学习下载。除了可运行的代码外资源还附带数据库初始脚本和部署调试服务说明能帮助读者快速搭建环境并理解PyQt5与MySQL连接的完整流程同时通过ui与py的对应关系掌握pyuic转换和界面逻辑分离的基本写法对完成课程设计或熟悉Qt开发流程具有直接参考价值。1. PyQt5 学生信息管理系统一套能跑通答辩和课设的现成骨架面对 PyQt5 学生信息管理系统这类 Python 毕业设计源码包很多人第一反应是双击 main.py 把它跑起来结果十有八九翻车。这套资源说白了就两部分Python 源码 SQL 脚本界面用 PyQt5 画数据落在 MySQL中间用 PyMySQL 建立连接覆盖登录、增删改查、模糊查询和统计这一类课设标准功能。我的建议是别急着启动先把数据库脚本导进去、把工程结构过一遍再运行半小时就能把环境踩平。适合正在赶课设、或者想拿现成骨架做二次开发的从业者也适合答辩前想补课的新手。2. 源码结构拆解一个文件一个职责先看清再动手拿到压缩包先别急着跑先看目录。这类课设工程的成熟模板一定不会把所有逻辑塞进一个文件——否则评审老师随口问一句「你这个查询结果是哪个方法刷出来的」你就答不上来。我拆这类工程时习惯先按「界面层、业务层、数据层」三条线把文件归好类再考虑运行。下面这套结构几乎所有 PyQt5 课设工程都会长成这样差异只在命名。2.1 三层模块划分界面、业务、数据各管一摊student_sys/ ├── main.py # 程序入口只负责启动 ├── db.py # MySQL 连接与通用查询封装 ├── ui/ │ ├── login_window.py # 登录窗口 │ └── main_window.py # 主窗口含所有功能页 ├── styles/ │ └── app.qss # 界面样式表 └── sql/ └── student_sys.sql # 建库建表脚本main.py 是入口它做的事只有三件加载配置、实例化登录窗口、进入 Qt 事件循环。db.py 承担所有 SQL 访问登录验证、学生增删改查都通过它转发。ui 目录下两个文件分别是登录框和主窗体主窗体里用 QTabWidget 或 QStackedWidget 承载多个功能页。styles 目录下的 QSS 文件控制全局配色sql 目录里则是完整建库脚本。如果想把功能页拆得更细常见做法是每个 Tab 页单独成一个文件例如 student_page.py、score_page.py。对于 2000 行以内的课设工程我建议不要拆太碎否则改一个字段要跨三个文件追反而麻烦。这套资源如果只有一个 main_window.py也别觉得简陋课程设计阶段「能跑、能讲、能改」比「架构炫」优先得多。2.2 信号槽按钮和表格之间是怎么联动起来的PyQt5 里最容易被答辩老师追问的就是信号槽机制。按钮被点击、表格行被双击、输入框内容变化这些「事件」通过信号广播你的处理方法通过槽函数接住。下面是主窗口里最常见的三组连接# 按钮点击事件绑定 self.btn_search.clicked.connect(self.on_search) self.btn_add.clicked.connect(self.on_add) # 表格双击事件双击某一行直接进入编辑 self.table.doubleClicked.connect(self.on_edit)clicked 是 QPushButton 内置信号on_search 是当前类里自定义的槽函数。connect 做的事情是「监听信号 → 收到后调用槽函数」。这里有两个易错点一是槽函数不要加括号self.on_search()会在程序启动时立刻执行一次而不是点击时执行二是 doubleClicked 信号会携带一个 QModelIndex 对象所以 on_edit 的定义要写成def on_edit(self, index)方法签名的参数数量要和信号参数对上否则运行时报错。我接手过的工程里最常见的低级错误是把所有按钮逻辑写成if __name__ __main__里的一长串。那不是信号槽是脚本。信号槽的价值在于解耦界面只管发信号业务逻辑只待在槽函数里后期加按钮、加功能不需要动结构。2.3 主窗体到子页面的切换方式主窗体里通常有多个功能页面切换方式直接决定代码复杂度。推荐用 QStackedWidget 做页面堆叠而不是每次点击都打开一个新窗口。新窗口弹来弹去学生信息还没看完窗口管理器先崩了。# 切换到第 2 个页面例如成绩查询页 self.ui.stackedWidget.setCurrentIndex(1)setCurrentIndex 接收页面的下标从 0 开始。用这个方式切换页面数据状态保留得最完整——从查询页切到编辑页再切回来表格内容还在原地。另一个常见方案是 QTabWidget适合功能 Tab 不用来回藏的场景。登录窗口切到主窗口则一般这么衔接self.login_success True self.main_window MainWindow() self.main_window.show() self.close()注意 close 之后进程不会退出因为登录窗口的引用还在 Qt 事件循环里。除非你想做「关闭登录框后整个程序退出」的假登录效果否则不要用sys.exit去关登录窗口那会让主窗口一起消失。3. 环境搭建与数据库初始化版本、字符集、授权三个变量拖垮一个课设工程的有三件事Python 版本不对、SQL 脚本没有正确导入、数据库账号连不上。这三件事的失败表现都极其相似——窗口起不来或者一登录就报pymysql.err.OperationalError。所以我把环境搭建单独拆出一章先把这条链路焊死再谈功能。3.1 依赖安装PyQt5 与 PyMySQL 的版本边界pip install PyQt55.15.10 PyMySQL1.1.0PyQt5 5.15 系列是最后一批广泛兼容的版本新项目我一般锁在这个版本上不要轻易追新。PyMySQL 是纯 Python 实现的 MySQL 驱动不需要本地编译在 Windows 上安装最省事。我在 Python 3.10、3.11 上都跑过这一套组合没有兼容性问题。注意别混装 PyQt5 和 PyQt6按钮、信号槽 API 有差异课设工程改了包名很容易牵一发动全身。安装完成后验证一下python -c import PyQt5; import pymysql; print(ok)如果这里报 ModuleNotFoundError说明 pip 装到了别的 Python 环境。这个现象在 macOS 和 Linux 上尤其常见系统自带的 Python 和当前终端用的 Python 不是同一个。检查pip --version和python --version是否对得上用同一个解释器再装一遍。3.2 导入 SQL 脚本utf8mb4 与账户授权打开 sql 目录下的 student_sys.sql你先看到的一般是建库语句CREATE DATABASE IF NOT EXISTS student_sys DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_sys; CREATE TABLE t_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(32) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL, role VARCHAR(8) DEFAULT admin ) ENGINEInnoDB; CREATE TABLE t_student ( id INT AUTO_INCREMENT PRIMARY KEY, sno VARCHAR(16) UNIQUE NOT NULL, name VARCHAR(32) NOT NULL, gender VARCHAR(4), class_name VARCHAR(32), phone VARCHAR(16), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB;建库时指定utf8mb4字符集是这批资源最值得保留的习惯。utf8mb4 是 MySQL 对 UTF-8 的完整实现能直接存中文也能兼容表情符号只写 utf8 会遇到四字节字符入库报错的问题。导入脚本可以用命令行mysql -u root -p student_sys.sql或者进入 MySQL 后用 source 命令mysql source C:/path/to/student_sys.sql;source 的路径在 Windows 下用正斜杠写复制文件资源管理器里的反斜杠路径进去会找不到文件。导入后检查三件事SHOW CREATE TABLE t_student确认字符集SELECT COUNT(*) FROM t_student确认初始数据存在SELECT * FROM t_user确认管理员账号已经预置。这三步对应后续登录能不能进去、列表有没有数据、账号对不对提前验证省得后面猜。3.3 数据库配置读取路径和账号写在哪里才是安全的这套工程的数据库连接配置一般集中在 db.py 或一个 config 文件里。课设阶段我最推荐直接写一个字典# db.py import pymysql config { host: localhost, port: 3306, user: root, password: 123456, database: student_sys, charset: utf8mb4 } def get_conn(): return pymysql.connect(**config)host 是localhost时MySQL 服务必须在本机启动不能是远端数据库地址。port 默认 3306如果你本机 MySQL 改过端口这里不改的话会报Connection refused。password 默认写 123456 这类初始密码实际导入后你应该用ALTER USER rootlocalhost IDENTIFIED BY 你的密码;改掉并且同步改这个字典。密码直接写死在代码里在真实项目里是忌讳但毕业设计场景里答辩老师反而会问「为什么不用配置文件」。所以我的习惯是源码里保留 config 字典注释里写清楚「上线前抽到 config.ini」给出如下拆分方案# config.ini [mysql] host localhost port 3306 user root password 123456 database student_sys charset utf8mb4读取时用configparser解析这样打包后改库密码不需要重新编译代码。不过课设阶段直接字典改成自己本机的账号密码比折腾配置文件读取更快。3.4 首次启动验证哪些表现说明环境搭对了环境搭完第一次启动不要直接点登录。先看有没有异常抛出再判断是不是配置问题。我列了一个自检表对应每种表现的各种可能性启动表现可能原因处理方式窗口正常出现登录报Access denied账号或密码错误用命令行mysql -u root -p验证账号可登录窗口正常出现登录报Unknown database库名不一致检查 config 里的 database 是否与建库语句一致窗口一闪而过直接退出缺依赖或代码里抛异常在终端里运行python main.py看完整报错界面无样式QSS 文件路径错误检查 styles.py 里的相对路径是否基于工程根目录我见过最气人的一种情况数据库没启动窗口能开一点登录直接崩。在 Windows 上可以到「服务」里确认 MySQL 是否处于运行状态macOS 则用brew services list查看。这一章整体走完后面所有功能调试才有一个稳定的地基。4. 核心功能闭环登录、增删改查与模糊查询的对接学生信息管理系统的功能面说起来就是一条线登录进去、看列表、按条件查、增一条、改一条、删一条。代码里界面层和 SQL 层一一对应把这条线串通就完全够答辩演示了。下面按功能顺序逐个拆。4.1 登录校验参数化查询与角色返回登录窗口拿到输入框的账号和密码不能直接拼 SQL 字符串必须走参数化查询。常见做法是把校验封装成一个方法返回用户角色或者 Nonedef check_login(self, username, password): sql SELECT id, role FROM t_user WHERE username%s AND password%s row self.db.fetch_one(sql, (username, password)) return row%s是占位符PyMySQL 会把元组里的值安全地填进去这是防 SQL 注入的标准姿势。如果写成WHERE username username 输入框里加个引号就能把 SQL 结构拆掉这个坑在答辩现场属于低端翻车必须避免。fetch_one 返回的是字典还是元组取决于 db.py 里 cursorclass 的设置这套资源如果你发现row[role]报 TypeError就去 db.py 把 cursorclass 改成pymysql.cursors.DictCursor。role 这个字段是有用的不只是摆设。管理员账号和学生账号可以共用一张用户表登录后根据 role 决定主窗口里「新增/删除」按钮是否可点击。界面隐藏不等于权限安全但课设层面已经够讲。4.2 表格填充从查询结果到 QTableWidget列表展示是学生信息系统的门面。查询接口返回一批记录要把它们填进 QTableWidget关键在于先清空旧数据再整表重建def load_table(self, rows): self.table.setRowCount(0) for i, row in enumerate(rows): self.table.insertRow(i) self.table.setItem(i, 0, QTableWidgetItem(str(row[sno]))) self.table.setItem(i, 1, QTableWidgetItem(row[name])) self.table.setItem(i, 2, QTableWidgetItem(row[gender]))setRowCount(0)先重置行数防止上一次查询的残留行继续显示。insertRow(i)每插入一行前先申请行号然后setItem按列填值。注意学号虽然可能是字符串但强烈建议str()包一层避免数据库里存了数字类型导致 QTableWidgetItem 类型报错。这个方法不适合大数据量5000 行以上会明显卡顿。课设几千行的数据量没问题但如果演示时硬塞 2 万条进去界面刷新会有半秒左右的白屏这不是 bug是逐单元格写入的固有瓶颈。防止卡顿的办法是后面避坑章里讲的setRowCount一次性分配合并行数这里先记个印象。4.3 增删改事务与确认弹窗新增和编辑走同一个逻辑框架弹一个对话框收集字段 → 组装 INSERT 或 UPDATE 语句 → 刷新表格。删除则必须有确认弹窗这是课设演示的加分项也是防止手滑删数据的防线def on_delete(self): reply QMessageBox.question( self, 确认删除, 确定要删除当前选中的学生吗该操作不可恢复。, QMessageBox.Yes | QMessageBox.No ) if reply QMessageBox.Yes: self.db.execute( DELETE FROM t_student WHERE id%s, (self.current_row_id,) ) self.load_data()删除条件一定要用主键id不能用学号、姓名。学号理论上唯一但实际库里可能因为早期数据不规范存在重复用姓名删除更是灾难。current_row_id 应该在双击表格时从选中行取到并暂存def on_edit(self, index): self.current_row_id self.table.item(index.row(), 0).data(32)item(...).data(32)是读取 QTableWidgetItem 的原始数据比.text()更稳避免显示格式和存储值不一致导致删错行。这个细节讲给答辩老师听对方会认为你真踩过数据库的坑。4.4 模糊查询LIKE 拼接与输入处理模糊查询是演示中最容易被现场造数据的环节。查询框里输入姓名或班级列表实时过滤。SQL 的写法核心是「动态条件拼接」def search(self, name_input, class_input): sql SELECT * FROM t_student WHERE 11 params [] if name_input: sql AND name LIKE %s params.append(f%{name_input}%) if class_input: sql AND class_name LIKE %s params.append(f%{class_input}%) self.load_table_with_sql(sql, params)WHERE 11是一条惯用技巧它让后续所有AND条件都能直接追加不需要判断「是不是第一个条件」。LIKE %s配合%关键字%实现包含匹配%在 SQL 里是通配符代表任意长度字符。name_input 必须经过 f-string 包一层%而不是直接在 SQL 里写LIKE %name_input%否则连模糊查询的字面意思都无法实现。两个输入框同时有值时params 列表里两个参数依次对应 SQL 中两个占位符顺序和 SQL 拼接顺序一致。搜索按钮连击多次不会累积条件因为每次都是重新组装 sql 变量。唯一要注意的是输入框里如果带了_或%字符会被当作通配符处理这个细节放到避坑章单独说。5. 避坑与常见问题排查五条血泪踩坑记录前面几章把工程结构和功能链路打通了这一章专门讲我实际调试这类课设工程时踩过的坑。每一条都按「现象 → 原因 → 解决」写清照着比对即可。5.1 中文乱码建库时字符集没定对现象程序能跑新增学生后姓名在界面上正常显示但去 MySQL 命令行查表中文全部变成??或者直接消失。原因数据库连接字符串里没有charsetutf8mb4或者建库语句没指定字符集。MySQL 默认字符集在部分安装环境里是 latin1中文当然存不进去。解决两处同时改——建库脚本里用DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci连接参数里加上charset: utf8mb4。如果是已经建好的库用ALTER DATABASE student_sys CHARACTER SET utf8mb4;补救。改完后重启程序再试不要只改一处。5.2 QSS 不生效样式加载时机和路径问题现象界面元素齐全、按钮能点但颜色、圆角、字体完全没有整个窗口灰扑扑的。原因app.qss 文件没有被加载或者加载时机早于窗口实例化样式被后续默认样式覆盖。相对路径写错更是常见窗口类里open(app.qss)用的是当前工作目录在终端直接运行和打包后运行的工作目录往往不是同一个。解决加载路径基于工程根目录动态拼并确保在窗口实例化之后立即应用import os, sys qss_path os.path.join(os.path.dirname(sys.argv[0]), styles, app.qss) with open(qss_path, encodingutf-8) as f: self.setStyleSheet(f.read())sys.argv[0]取到的是入口脚本所在目录不管从哪个目录启动都不会走偏。注意open要指定encodingutf-8Windows 下 QSS 文件里如果含中文注释不指定编码会直接抛 UnicodeDecodeError。5.3 打包后双击没反应PyInstaller 吞了错误现象开发环境运行正常用 PyInstaller 打包后双击 exe 毫无反应进程一闪而过或压根不出现。原因-w参数隐藏了控制台窗口所有异常信息也一起被吞了程序启动即崩溃但你什么都看不见或者是 PyMySQL、PyQt5 的某些动态库没被正确打进包里。解决第一轮打包先不加-w用python -m PyInstaller -F main.py生成带控制台的版本双击后看终端里的完整报错。报错指向哪就补哪缺包就加--hidden-import。常见需要补的pyinstaller -F -w --hidden-import pymysql --hidden-import PyQt5.QtSvg main.py打包后还需要把 styles 目录和 sql 目录复制到 exe 同目录。PyInstaller 的-F只打包 Python 字节码和依赖库外部资源文件必须手动带。这也是我建议把 QSS 路径写成基于sys.argv[0]的原因——打包后工作目录会变写死相对路径必挂。5.4 查询卡顿QTableWidget 逐行 insertRow 太慢现象数据量到两三千行每次刷新表格要卡一两秒点击搜索按钮明显有停顿感。原因insertRow每调用一次就要触发表格重排2000 行等于重排 2000 次。这是 QTableWidget 的典型性能玄学初学者最容易踩上。解决一次性setRowCount分配合适行数再逐行setItem一次批量填充self.table.setRowCount(len(rows)) for i, row in enumerate(rows): self.table.setItem(i, 0, QTableWidgetItem(str(row[sno]))) self.table.setItem(i, 1, QTableWidgetItem(row[name]))另外在数据量大的表上给数据库查询加索引也有效。t_student 表如果经常按 name 查建一个普通索引就行CREATE INDEX idx_student_name ON t_student(name);两件事一起做几千行的量级能到毫秒级刷新。5.5 输入框里的 % 和 _ 被当通配符搜索跑偏现象搜索框输入%或者_列表返回所有数据明明只输了一个字符却像没过滤一样。原因这两个字符在 LIKE 语句里是通配符%匹配任意串_匹配单个字符。用户搜索字符串里如果包含这些符号不转义就会被当成语法的一部分。解决把%、_、\三个字符做转义再把转义后的关键字传入 LIKE 条件def escape_like(s): return s.replace(\\, \\\\).replace(%, \\%).replace(_, \\_)SQL 里写成name LIKE %s ESCAPE \\告诉 MySQL 用什么字符作为转义标记。这一招在答辩现场遇到较真的老师提问「你的搜索有注入风险吗」时非常加分。别觉得输入%是无聊场景评审现场真的有人会这么试。6. 答辩演示前的十分钟验证清单把翻车空间压到零6.1 固定顺序检查从数据库到界面演示前一天我一般不再改任何代码只做一遍「固定顺序验证」。顺序是确认 MySQL 服务启动 → 用命令行登录数据库确认账密可用 → 运行程序进入主界面 → 点一次查询按钮确认列表有数据 → 执行「新增 → 修改 → 删除」最小闭环。每个环节录一个预期结果和实际表现比对检查步骤预期结果MySQL 服务启动服务状态为 running命令行登录能进 mysql 命令提示符程序登录无异常进入主窗口点击查询表格出现初始数据新增一条记录表格末行出现新记录删除该记录表格行数回落这六步走完环境、依赖、数据库、代码四大环节就都有了真实证据。演示时最忌讳的是一边打开程序一边手动 import 数据库或改配置现场任何一个不确定项都会放大为灾难。6.2 数据复位脚本演示的后悔药课设答辩少不了现场演示新增、修改、删除演示完数据往往已经一团糟。我的习惯是在 sql 目录里放一个 reset.sql里面只有三条语句清空业务表、重置自增值、重新插入初始数据。USE student_sys; SET FOREIGN_KEY_CHECKS 0; TRUNCATE TABLE t_student; SET FOREIGN_KEY_CHECKS 1; INSERT INTO t_student (sno, name, gender, class_name, phone) VALUES (20230001, 示例学生, 男, 计科2301, 13800000000);模拟数据复位后再跑一遍主界面查询列表就回到了演示前的样子。我不会在演示时真的执行这个脚本但会把它放在桌面上——万一演示到一半出了岔子「数据恢复」就是最快下台阶的路。那一次我在演示查询功能时输入框漏了半角空格列表直接空白台上愣了几秒才反应过来是输入问题。从那以后我每交付一个课设工程都会强制把这个验证清单和 reset.sql 塞进工程根目录——不是给代码看的是给演示前的自己看的。希望帮到你。本文还有配套的精品资源点击获取