
简介面向计算机科学与技术专业学生毕业设计/课程设计的C Qt学生信息管理系统项目包以Qt构建图形界面、MySQL作为数据存储完整覆盖数据库设计、界面布局、SQL增删改查、事件绑定与异常处理等环节适合希望系统提升C与数据库应用能力的开发者参考。压缩包共79个文件包括26个cpp源码、25个h头文件、23个ui界面文件另含pro工程文件、sql建表脚本、md说明与docx使用报告整体大小212KB结构清晰便于工程导入和学习。已有710人学习下载。项目中内置学生、课程、宿舍、缴费、奖惩等管理模块并附有完整的毕业设计报告与数据库脚本既能帮助快速搭建可运行的学生信息管理Demo也能为毕业设计文档编写、答辩演示和功能扩展提供直接参考。1. 毕设项目是C Qt与MySQL的组合其实并不复杂把 Mysql 集成进界面系统是主要工作量学生信息管理系统可分解成三个问题数据库建模与访问、Qt界面交互、发布部署。第一点决定数据存取的稳定性第二点决定演示效果第三点决定能否交得出差。框架选择上Qt 5.15 LTS稳定MySQL 8.0普遍驱动用QSqlDatabaseORM可选QSqlQuery直接执行SQL。要避免直接把SQL字符串拼进槽函数后续维护很麻烦。真正需要想清楚的是怎么把查询出来的数据放到表格里删除一条学生记录时如何处理关联成绩表以及在没有安装Qt的环境中如何跑起来。2. Mysql数据库与Qt项目的环境适配思路里常见问题是驱动版本不匹配和编码不一致系统要先建库建表再通过Qt连接最后处理中文乱码。很多人的毕设一上来就写界面结果连不上数据库后面全部返工。正确顺序是先让命令行能与 MySQL 正常通信再写 Qt 代码。2.1 数据库基础student表给出结构包含 id、name、gender、major、phone、birth_date、enroll_year 等字段成绩表 score_info 关联 student_id 与 course_id学生信息管理系统的核心数据由两部分组成学生基本信息和课程成绩。把这两类数据放进同一张表会带来大量冗余所以需要拆成两个表。student 表存储学生的自然属性score 表负责记录多门课程的成绩通过学号把两张表关联起来。CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE student_db; CREATE TABLE IF NOT EXISTS student ( student_id VARCHAR(20) PRIMARY KEY, name VARCHAR(50) NOT NULL, gender ENUM(男,女) DEFAULT 男, major VARCHAR(100), class_name VARCHAR(50), phone VARCHAR(11), birth_date DATE, enroll_year YEAR ) ENGINEInnoDB; CREATE TABLE IF NOT EXISTS score ( id INT AUTO_INCREMENT PRIMARY KEY, student_id VARCHAR(20) NOT NULL, course_name VARCHAR(100) NOT NULL, score DECIMAL(5,1), FOREIGN KEY (student_id) REFERENCES student(student_id) ON DELETE CASCADE ) ENGINEInnoDB;上面的脚本里“CREATE DATABASE IF NOT EXISTS”只会创建一次多次执行不会报错适合在 Qt 启动时预检数据库中自动调用。student_id 用 VARCHAR(20) 而不是 INT因为很多学校的学号带字母或前导零整数类型会把这些信息丢掉。ENUM 字段在 Qt 表格里显示中文绑定数据时注意传给 QSqlTableModel 的值要与枚举定义一致。score 表使用 AUTO_INCREMENT 作为内部主键是为了避免一个学生选多门课时 student_id 重复导致主键冲突。外键带 ON DELETE CASCADE删除学生时数据库会自动清理该生的所有成绩记录省得在 Qt 代码里手动先删成绩再删学生。2.2 在 Windows 下安装 MySQL 8.0 并配置 Qt 5.15 的连接驱动注意 MSVC 与 MinGW 的驱动文件路径不同需要将 libmysql.dll 放到正确位置MySQL 安装配置教程中经常强调字符集安装时选择 Server only 后配置类型选 Development Machine 即可重点是字符集必须选 utf8mb4。如果安装时选错后面可以统一改表但连接层的字符集还会出问题。Qt 官方在 Windows 上提供 MSVC 和 MinGW 两套编译版本MySQL 官网下载的 C 连接器是 MSVC 编译的两者不能随意互用。驱动不匹配是毕设最常见问题表现为 QSqlDatabase 查询时报 “Driver not loaded”。要确认当前 Qt 是否能加载 MySQL 驱动可以先用一段代码列出所有可用驱动QStringList drivers QSqlDatabase::drivers(); qDebug() drivers;如果列表里包含 QMYSQL说明驱动文件存在如果只有 QSQLITE说明 qsqlmysql.dll 缺失或路径不对。缺驱动时有两个处理方式一是把 Qt 安装目录中 plugins/sqldrivers/qsqlmysql.dll 和 MySQL 的 libmysql.dll 复制到程序可执行文件旁二是重新编译驱动但毕设场景下复制文件更快。注意 Qt 套件必须与编译环境一致用 MSVC 编译的 Qt 项目不能搭配 MinGW 的插件文件否则会出现运行时崩溃。2.3 连接字串与编码配置在代码中设置连接参数并使用 QSqlQuery 执行 DDL 和 DML随后检查错误细节连接 MySQL 时用到 QSqlDatabase 单例模式。每次 addDatabase 使用不同的连接名避免多个窗口同时打开默认连接带来的冲突。QSqlDatabase db QSqlDatabase::addDatabase(QMYSQL, main_conn); db.setHostName(127.0.0.1); db.setPort(3306); db.setDatabaseName(student_db); db.setUserName(root); db.setPassword(your_password); if (!db.open()) { qDebug() open error: db.lastError().text(); return; } QSqlQuery query(db); query.exec(SELECT COUNT(*) FROM student); if (query.next()) { qDebug() total: query.value(0).toInt(); }这里关键点是 open 失败后 lastError().text() 的输出。如果是 “Access denied”先检查密码或用户名如果是 “Unknown database”说明 student_db 还没有创建可以先不指定数据库名连接成功后用 SQL 建库。端口参数传递 3306 即可注意不要写成字符串Qt 会自动转换。驱动连接成功后执行 SET NAMES utf8mb4 也可以但与数据库默认字符集重复设置没有明显副作用。错误排查时先用 MySQL Workbench 或命令行确认同样的连接参数能够工作这样能快速把问题定位到 Qt 侧还是 MySQL 侧。3. 用 Qt 构建学生信息管理系统的数据层与界面层数据层封装再界面调用核心是 QSqlTableModel 与信号槽联动界面层负责录入与显示数据层负责查询与提交。直接用 QSqlQuery 写每次增删改并不是不行但 QSqlTableModel 可以大幅减少代码量。表格控件与数据库表形成映射关系单元格编辑完成后可以直接提交。3.1 QSqlTableModel 与 QTableView 的组合用法直接修改表格内容即可同步数据库避免手写大量 UPDATE 语句QSqlTableModel 是 Qt 提供的表模型它与数据库表一一对应并提供 select、insert、delete、submit 等接口。用它操作学生表非常自然model new QSqlTableModel(this, db); model-setTable(student); model-setEditStrategy(QSqlTableModel::OnManualSubmit); model-select(); model-setHeaderData(0, Qt::Horizontal, 学号); model-setHeaderData(1, Qt::Horizontal, 姓名); ui-tableView-setModel(model);setEditStrategy 选择 OnManualSubmit这样用户在表格里改数据后只有点击“保存”按钮才真正写入数据库避免点到其他单元格时立刻更新落库。调用 submitAll 时如果有一条记录提交失败整个事务回滚不会出现一半数据写进去一半没有写。添加一条新记录的代码是int row model-rowCount(); model-insertRow(row); model-setData(model-index(row, 0), ui-newIdEdit-text()); model-setData(model-index(row, 1), ui-newNameEdit-text());这里有一个看不见的坑student_id 是主键insertRow 之后如果学号输入为空或重复submitAll 会在数据库层报错Qt 这边只能拿到通用错误信息看不到具体字段。因此在提交之前要做一次重复检查可以通过 QSqlQuery 先查到现有学号集合再与将要插入的数据比对。删除学生时直接用 removeRow 然后 submitAll外键的级联删除会自动清理 score 表中的成绩记录这一点在设计表时就避免了遗留孤儿数据。3.1.1 在按钮槽中实现添加、删除、修改的逻辑添加、删除、修改三个操作集中在三个槽函数里界面按钮通过 clicked 信号连接。修改不是单独按钮而是依赖 QTableView 的编辑功能用户双击单元格即可修改需要保存时点击“保存”按钮。void MainWindow::onSave() { if (!model-submitAll()) { qDebug() submit error: model-lastError().text(); model-revertAll(); } else { model-submitAll(); ui-statusBar-showMessage(保存成功, 3000); } } void MainWindow::onDelete() { QModelIndex cur ui-tableView-currentIndex(); if (!cur.isValid()) { QMessageBox::warning(this, 提示, 请先选择要删除的行); return; } model-removeRow(cur.row()); }代码中的 model-lastError() 是 QSqlError 类型直接转 text 输出即可。删除时 currentIndex 返回无效索引意味着点击的是表格空白区域这时应该提示用户而不是直接执行删除。revertAll 只回滚还没有提交到数据库的修改已经 submitAll 的数据无法撤销这是设计上的注意点。对毕设来说提交前弹窗让用户确认会很加分但确认框不要放在槽函数内部可以单独抽成一个判断函数。3.2 用 QSqlQuery 实现按学号与姓名模糊查询并支持排序前端通过行编辑框输入条件QSqlTableModel 虽然方便但自定义查询条件时太受限。比如按学号模糊查询可以选择 setFilter 方式也可以换用 QSqlQueryModel。QSqlQueryModel 是只读模型适合显示查询结果不直接编辑。void MainWindow::onSearch() { QString keyword ui-searchEdit-text().trimmed(); QSqlQuery query(db); query.prepare(SELECT student_id, name, gender, major, class_name FROM student WHERE student_id LIKE :kw OR name LIKE :kw ORDER BY enroll_year DESC); query.bindValue(:kw, % keyword %); if (!query.exec()) { qDebug() query.lastError().text(); return; } QSqlQueryModel *resultModel new QSqlQueryModel(this); resultModel-setQuery(std::move(query)); ui-tableView-setModel(resultModel); }代码中 LIKE 语句加上两个百分号可以匹配学号或姓名的任意位置例如输入“张”能把“张三”“老张头”都查出来。查询模型替换了原来的表格模型后之前编辑功能会失效这是正常的因为查询结果本身是只读视图。ORDER BY enroll_year DESC 让新入学的学生排在最前面方便老师查看最近一届数据。如果想回到全部列表重新执行 model-select() 并把模型换回 QSqlTableModel 即可。3.2.1 模糊查询代码与参数说明bindValue 绑定参数的做法是为防止 SQL 注入毕设中直接拼字符串也能运行但不建议。用占位符后即使关键字包含单引号也不会破坏 SQL 结构。查询结果为空时QTableView 会显示空行不会报错需要另一段判断逻辑提示“没有找到匹配记录”可以使用 query.next() 判断。关于列头显示QSqlQueryModel 默认列名来自数据库字段名直接显示英文不好看。给列设置别名更有效查询中用 AS 重命名例如 SELECT student_id AS 学号, name AS 姓名。但 QTableView 绑定模型后列名会直接变成中文若通过 setHeaderData 会失效应当优先选择 SQL 别名方案。系统常态下默认表格显示 student 表全部内容搜索时替换成查询模型搜索结束后重新加载。3.3 信号槽与数据刷新一致性多个窗口共用同一个数据库连接使用全局单例管理连接系统如果包含“学生信息管理”和“成绩录入”两个窗口每个窗口各自调用 addDatabase 会创建多个物理连接浪费资源且容易踩并发修改的问题。合理设计是让整个程序共享一个 QSqlDatabase。class DbManager { public: static QSqlDatabase getConnection() { if (QSqlDatabase::contains(main_conn)) { return QSqlDatabase::database(main_conn); } QSqlDatabase db QSqlDatabase::addDatabase(QMYSQL, main_conn); db.setHostName(127.0.0.1); db.setPort(3306); db.setDatabaseName(student_db); db.setUserName(root); db.setPassword(your_password); if (!db.open()) { qDebug() open failed: db.lastError().text(); } return db; } };利用 Qt 的连接池特性每次通过 QSqlDatabase::database(main_conn) 获取同一个连接句柄不需要重复打开。在子窗口对数据库做出改变后主窗口需要刷新时直接 model-select()但要注意该操作必须是在同一个连接对象上执行。事务操作时两个窗口同时提交后提交的一方可能会覆盖前面的记录毕设中减少这种风险的办法是提交前都重新 select 一次。4. 发布与常用排错场景Qt 程序在另一台电脑上运行时的常见问题以及 MySQL 驱动缺失的解决步骤能把系统在开发机上跑通只是第一步交毕设或者给老师演示时经常换到一台没有 Qt 环境的电脑上问题会在发布环节集中爆发。打包过程有固定的动作不能只复制 exe。4.1 用 windeployqt 工具生成 exe 的依赖文件注意 Qt 版本与编译器配套并把 MySQL 的 dll 一起放进部署目录发布流程从 Release 构建开始。打开 Qt 提供的命令行工具例如 “Qt 5.15.2 (MSVC 2019 64-bit)”进入 Release 输出目录。运行windeployqt StudentSystem.exewindeployqt 会递归复制 Qt 运行时、平台插件、样式插件。但有两个东西它不会自动处理一是 MySQL 驱动二是 libmysql.dll。手动复制时从 Qt 安装路径下找到 plugins/sqldrivers/qsqlmysql.dll复制到发布目录的 sqldrivers 子目录下。然后从 MySQL 安装目录的 lib 下复制 libmysql.dll放到 exe 同级目录。两个文件缺一不可否则最终运行时会出现 “Driver not loaded”。常见错误清单如下错误现象原因处理方法程序启动提示缺少 Qt5Core.dllwindeployqt 未成功或平台插件缺失重新执行 windeployqt确认生成 platforms 目录QSqlDatabase: QMYSQL driver not loaded缺少 qsqlmysql.dll 或 libmysql.dll检查 sqldrivers 目录及 libmysql.dll 位置Access denied for user ‘root’密码错误或 root 权限限制核对 MySQL 账号密码确认 host 为 127.0.0.1Unknown database未建库或库名不匹配在 MySQL 中执行建库脚本Can’t connect to MySQL serverMySQL 服务未启动或端口被防火墙拦截打开服务管理器启动 MySQL80放行 3306windeployqt 生成的目录里还包括 translations 文件夹保留它用于 qt 国际化支持。如果程序界面显示中文但按钮文字变成乱码多半是源文件编码问题需要将 .cpp 文件另存为 UTF-8 with BOM。4.2 中文乱码的两处根源外部数据源编码与 Qt 内部显示编码建议统一为 UTF8Qt 5 内部使用 Unicode中文处理本身没大问题。乱码通常出在三个环节源文件编码、数据库表字符集、控制台输出。源文件用 GB2312 或 ANSI 保存时编译器 VS 会按本地代码页解释导致中文字符串显示为“鎴戠殑”之类的内容。解决方案是统一用 UTF-8 with BOM并添加预处理指令查看编译器警告。数据库表字符集配置为 utf8mb4 后Qt 连接时不需要额外转换。但需要注意MySQL 连接层默认字符集可能与表格不一致。可以在连接后执行SET NAMES utf8mb4;这条语句可以放在连接成功后执行也可以作为一条 QSqlQuery 调用。如果还是乱码检查 Windows 区域设置是否使用了 Unicode UTF-8在“控制面板-区域”中勾选“Beta使用 Unicode UTF-8 提供全球语言支持”后重启很多诡异的乱码问题会消失。毕设中不建议通过 QString::fromLocal8Bit 来处理那只是掩盖了编码不一致的根源。5. 学生信息管理系统的进阶用法主要有三条打包、加密连接配置与报表输出。这三项会让系统从能用变为好用当基础增删改查完成可以加入一些能够提升完成度的功能同时保持代码量可控。下面三个方向任选其一都能让系统观感有明显提升。5.1 把密码加密存储到配置文件中不再写在代码里每次运行时读取配置文件代码中写死数据库密码在开发时方便但系统一旦给老师或同学演示源码会被看到数据库密码就泄露了。常见做法是把连接参数移到一个 ini 文件程序启动时读取。另一个层面本地配置文件本身就是明文所以还需要对密码做简单混淆。使用 QCryptographicHash 对密码进行 SHA-256 加盐处理然后保存哈希值连接数据库时把用户输入的密码做同样处理比对哈希是否一致这样可以保护密码不被直接读出来而真正用于连接数据库的密码密钥可以用 macOS 钥匙串或 Windows 凭据管理器存储但实现成本较高不建议在毕设中引入。可行的方式是提供一个配置文件里面存放 MySQL 用户名和密码。使用 QSettings 读取QSettings settings(config.ini, QSettings::IniFormat); settings.beginGroup(mysql); QString host settings.value(host, 127.0.0.1).toString(); int port settings.value(port, 3306).toInt(); QString user settings.value(user, root).toString(); QString password settings.value(password).toString(); settings.endGroup();参数后面的默认值省去了手工填写的步骤host 默认 127.0.0.1port 默认 3306。密码字段在部署时由人工写入。配置文件与 exe 放在同一目录程序启动时如果找不到可以直接用写死的默认值再给出“配置文件缺失使用默认连接参数”的提示这样能兼容不同使用环境。5.2 导出一份包含学生信息和成绩统计的 Excel 或 PDF 报表能够提升系统完成度在 Qt 中输出 Excel 有几种路径若目标机器安装了 Office可以用 QAxObject 操作 Excel COM 组件编程灵活但依赖 Office如果不依赖 Office可以使用 CSV 文件Excel 也能打开通用性好且代码量少。生成 CSV 的核心是读取数据库内容并组合写文件QFile file(students_report.csv); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { return; } QTextStream out(file); out.setEncoding(QStringConverter::Utf8); out 学号,姓名,专业,班级\n; QSqlQuery query(db); query.exec(SELECT student_id, name, major, class_name FROM student); while (query.next()) { out query.value(0).toString() , query.value(1).toString() , query.value(2).toString() , query.value(3).toString() \n; } file.close();上述代码使用 QTextStream 并设置 UTF-8 编码生成的 CSV 文件用 Excel 打开时中文能正常显示。如果把内容变成绩统计SQL 中可以用 GROUP BY 和 AVG 计算各课程平均分然后把结果写入 CSV 另一列。如果需要真正的图表展示推荐 Qt Charts 模块在 .pro 文件里添加 QT charts然后使用 QChart 显示柱状统计图能直观展示各课程平均分差异。5.3 给系统加上一份自检脚本启动时验证 MySQL 驱动、连接与数据表完整性减少现场排错时间系统接手另一台电脑运行前任何人都无法预判目标环境的 MySQL 服务是否启动、驱动是否完整。因此程序启动后立即执行一次自检错误信息直接弹窗或写入日志能省去很多现场排查时间。自检顺序先驱动、再连接、后检查表void MainWindow::selfCheck() { if (!QSqlDatabase::isDriverAvailable(QMYSQL)) { QMessageBox::critical(this, 驱动错误, QMYSQL 驱动不可用请检查 Qt 插件目录); return; } QSqlDatabase db DbManager::getConnection(); if (!db.isOpen()) { QMessageBox::critical(this, 连接失败, db.lastError().text()); return; } QSqlQuery query(db); if (!query.exec(SELECT COUNT(*) FROM student)) { QMessageBox::critical(this, 表缺失, student 表不存在请执行建表脚本); return; } }isDriverAvailable 检查的是当前 Qt 编译是否包含了 MySQL 驱动连接失败再检查 MySQL 服务状态表缺失则提示执行建表脚本。自检函数在构造函数中使用 QTimer::singleShot(0, this, MainWindow::selfCheck) 调用让界面先显示出来然后再进行耗时操作。状态栏可显示最终通过自检的学生数量如“共加载 128 条学生记录”用户一眼就知道数据是否完整。这套流程之后即使在另一台电脑上部署最多只需要调整 config.ini 里的密码剩下的错误会直接指出具体瓶颈减少无头绪的日志翻找。本文还有配套的精品资源点击获取