简介:本资源为基于Qt与MySQL的医院预约管理系统完整项目包,面向计算机相关专业的在校学生、教师及企业开发者,可用于毕业设计、课程设计、作业提交或项目初期立项演示。项目已通过导师指导与答辩评审,获得95分高分,代码经过测试运行,功能完整可靠。压缩包共47个文件,约31KB,包含15个cpp源文件、15个h头文件、12个ui界面文件,以及pro工程文件、user配置、README说明和授权码文本等,覆盖登录、注册、挂号、选科室、选时间、医生与管理员管理等模块,目录结构清晰。已有62人学习关注。读者可获得完整可运行的源码、数据库管理逻辑、界面设计文件与项目文档,既能直接用于毕设课设,也可在此基础上修改扩展新功能,适合Qt与MySQL入门进阶学习。
1. 从一份 Qt+Mysql 门诊预约源码说起:它到底能跑通什么
如果你正在找一套能直接跑起来的 C++ 桌面端项目,又恰好被 Qt 的信号槽和 MySQL 的连接配置折腾过,那这份基于 Qt+Mysql 的医院预约管理系统源码包值得先看一眼。它不是那种只丢几个 cpp 文件让你自己拼的“半成品”,而是把登录、注册、科室选择、时间选择、医生排班、患者挂号、管理员后台这些门诊流程里最核心的环节都做成了可编译的工程。整个项目用 qmake 组织,.pro文件里把 Qt 的模块依赖写得比较清楚,数据库层单独抽了databasemanager来管连接和查询,界面用.ui文件配合手写逻辑,属于典型的课程设计/毕设级桌面应用结构。
适合谁?计算机相关专业做课设、毕设的学生,或者想拿一个完整 Qt 项目练手信号槽、多窗口跳转、数据库增删改查的开发者。它解决的不是“高并发挂号”这种生产级问题,而是“怎么把一套带图形界面、带数据库持久化、带角色权限的桌面系统从零搭出来”的教学级需求。源码包里除了代码还有详细文档和授权说明,对照着复现的人比较友好。下面我按实际拆包和编译的顺序,把这份资源从环境到跑通、再到改功能的路子捋一遍。
2. 环境搭不起来,后面全是白搭:Qt 版本、MySQL 驱动与 .pro 配置
2.1 为什么 Qt 版本和编译器组合要先定死
这份工程用的是 qmake 构建,.pro文件里没有写死 Qt 的具体小版本,但实际编译时 Qt 5 和 Qt 6 的差异会直接导致报错。常见做法是优先用 Qt 5.14 或 Qt 5.15 配 MinGW 编译器,因为工程里的.ui文件和QSqlDatabase相关调用在 Qt 5 下最稳。如果你用 Qt 6,QTextCodec这类头文件可能已经移除,而老项目里经常顺手就 include 了。另一个坑是编译器位数:MySQL 的 C 连接库分 32 位和 64 位,Qt 的 MinGW 也分 32/64,两边必须一致,否则编译能过、运行时报“driver not loaded”。
我一般会先确认三件事:Qt Creator 的 Kit 里编译器是 MinGW 64-bit 还是 32-bit;MySQL 安装的是哪个版本;libmysql.dll和libmysqld.dll放在哪个目录。这三者对齐了,后面才谈得上连数据库。
2.2 把 MySQL 驱动塞进 Qt 的正确姿势
Qt 默认不带 MySQL 驱动,需要自己编译或找现成的qsqlmysql.dll。源码包里通常不会附带这个驱动,所以这是第一个要自己补的环节。步骤不复杂,但顺序错了就会一直报QMYSQL driver not loaded。
# 先确认 Qt 安装目录下的 sqldrivers 文件夹里有没有 qsqlmysql.dll # 常见路径(以 MinGW 64-bit 为例): # C:\Qt\5.15.2\mingw81_64\plugins\sqldrivers\ # 如果没有,需要自己编译 Qt 的 MySQL 驱动插件 # 进入 Qt 源码的 sqldrivers/mysql 目录,用 qmake 生成 Makefile cd C:\Qt\5.15.2\Src\qtbase\src\plugins\sqldrivers\mysql qmake -- MYSQL_INCDIR="C:/Program Files/MySQL/MySQL Server 8.0/include" ^ MYSQL_LIBDIR="C:/Program Files/MySQL/MySQL Server 8.0/lib" mingw32-make mingw32-make install这段命令的核心是告诉 qmake 两件事:MySQL 头文件在哪、库文件在哪。MYSQL_INCDIR指向mysql.h所在目录,MYSQL_LIBDIR指向libmysql.lib所在目录。编译完成后,qsqlmysql.dll会被安装到 Qt 的plugins/sqldrivers下。注意:光有 dll 还不够,运行时libmysql.dll必须能被找到,通常把它复制到可执行文件同目录,或者加到系统 PATH 里。
提示:如果你用的是 MySQL 8.0,认证插件默认是
caching_sha2_password,而老版 Qt 驱动可能只认mysql_native_password。连不上时先改用户认证方式,别急着怀疑代码。
2.3 .pro 文件里必须检查的几行
打开outpatients-appointment-system.pro,重点看QT +=这一行有没有包含sql。如果只有core gui widgets,那QSqlDatabase相关类根本链接不进来。另外TARGET和TEMPLATE决定了生成的可执行文件名和工程类型,一般不用动。SOURCES和HEADERS里列出的文件如果和实际目录对不上,编译会直接报找不到文件,这时候要么改路径,要么把文件挪到对应位置。
QT += core gui sql greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = outpatients-appointment-system TEMPLATE = app这三行是工程能编译和连库的基础。sql模块负责数据库连接,widgets负责界面控件。如果编译时报undefined reference to QSqlDatabase::...,九成是这里漏了sql。
3. 从登录到挂号:核心模块的代码走读与数据库表对应关系
3.1 登录与注册:信号槽怎么把界面和数据库串起来
login.cpp和register.cpp是入口逻辑。登录界面收集用户名和密码后,通过databasemanager去查数据库,匹配成功就跳转到主窗口。这里的关键不是界面长什么样,而是信号槽的连接方式:按钮的clicked()信号连到自定义槽函数,槽函数里再调数据库查询。这种写法在 Qt 里很标准,但新手容易在“查询结果怎么传回界面”这一步卡住。
// login.cpp 里典型的登录校验逻辑 void Login::on_loginButton_clicked() { QString username = ui->usernameEdit->text().trimmed(); QString password = ui->passwordEdit->text().trimmed(); // 通过 DatabaseManager 单例查询用户表 QSqlQuery query; query.prepare("SELECT role FROM user WHERE username = ? AND password = ?"); query.addBindValue(username); query.addBindValue(password); if (query.exec() && query.next()) { QString role = query.value("role").toString(); // 根据角色跳转到不同窗口 if (role == "admin") { adminWindow = new Administrator(); adminWindow->show(); } else { mainWindow = new MainWindow(); mainWindow->show(); } this->close(); } else { QMessageBox::warning(this, "登录失败", "用户名或密码错误"); } }这段代码里prepare加addBindValue是防 SQL 注入的标准写法,比直接拼字符串安全。query.next()判断有没有查到记录,查到就取role字段决定跳转目标。参数说明:usernameEdit和passwordEdit是.ui文件里定义的控件对象名,如果你改了界面控件的 objectName,这里也要同步改,否则编译能过但运行时空指针。
3.2 科室与时间选择:多窗口跳转的数据传递
choosedepartment.cpp和choosetime.cpp负责让患者选科室和就诊时间。这两个模块的难点不在界面,而在窗口之间怎么传数据。常见做法是用构造函数传参,或者用全局变量。这份工程里我看到的做法是:选择科室后把科室 ID 存到一个成员变量,再打开时间选择窗口时带过去。
// choosedepartment.cpp 里选中科室后打开时间选择窗口 void ChooseDepartment::on_departmentList_itemClicked(QListWidgetItem *item) { // 从 item 的 data 里取出科室 ID int deptId = item->data(Qt::UserRole).toInt(); QString deptName = item->text(); // 把科室信息传给时间选择窗口 ChooseTime *timeWindow = new ChooseTime(deptId, deptName); timeWindow->show(); this->hide(); }这里Qt::UserRole是 Qt 预留的自定义数据角色,用来存数据库里的科室 ID,界面上只显示科室名称。这样既不影响用户看,又能把 ID 带进下一步。ChooseTime的构造函数接收两个参数,在它内部再根据科室 ID 查该科室下有哪些医生、哪些时间段可约。如果你要改成“先选医生再选时间”,改这里的跳转顺序和查询条件就行。
3.3 数据库表结构:至少要有哪几张表
虽然源码包里没有单独的.sql建表文件,但从代码里的查询语句可以反推出核心表结构。常见做法是至少建四张表:用户表、科室表、医生表、预约记录表。下面这张表是我根据代码逻辑整理的字段对应关系,实际建表时字段名要和代码里的 SQL 保持一致。
| 表名 | 关键字段 | 用途 | 代码中对应查询 |
|---|---|---|---|
| user | id, username, password, role | 登录与角色判断 | login.cpp 登录校验 |
| department | id, name | 科室列表展示 | choosedepartment.cpp 查询 |
| doctor | id, name, dept_id, schedule | 医生信息与排班 | choosetime.cpp 按科室查医生 |
| appointment | id, patient_id, doctor_id, time, status | 预约记录 | tiingzhen.cpp 挂号写入 |
建表时注意role字段用字符串存admin和patient,代码里就是按字符串比较的。appointment表的status可以存booked、cancelled这类状态,方便后续做取消预约功能。如果你要加“科室简介”或“医生职称”,在对应表里加字段,然后在界面代码里多取一个值就行。
4. 编译通过只是开始:运行期连库失败与界面卡死的排查
4.1 连库失败的三种典型现象与定位方法
第一种现象:程序启动就弹窗说QMYSQL driver not loaded。这说明 Qt 根本没找到 MySQL 驱动插件,去 Qt 安装目录的plugins/sqldrivers下看有没有qsqlmysql.dll,没有就按第 2 章的步骤编译。第二种现象:驱动加载了,但QSqlDatabase::open()返回 false。这时候用db.lastError().text()把错误打出来,常见的是用户名密码错、主机地址不对、或者 MySQL 服务没启动。第三种现象:编译时链接报错undefined reference to mysql_init,说明.pro里没有链接 MySQL 的库,需要在LIBS里加上-lmysql。
// 在 databasemanager.cpp 里加错误输出,方便定位 QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("127.0.0.1"); db.setPort(3306); db.setDatabaseName("hospital_db"); db.setUserName("root"); db.setPassword("your_password"); if (!db.open()) { qDebug() << "数据库连接失败:" << db.lastError().text(); return false; }qDebug()输出的错误信息会出现在 Qt Creator 的应用程序输出窗口里,比弹窗更详细。setHostName用127.0.0.1比localhost更稳,因为某些环境下localhost会走 socket 而不是 TCP。setPort默认 3306,如果你装 MySQL 时改过端口,这里要同步改。
4.2 界面卡死与中文乱码:两个高频翻车点
界面卡死通常发生在数据库查询耗时较长时,主线程被阻塞,窗口就无响应了。这份工程里的查询都是同步的,数据量小的时候感觉不到,一旦预约记录多了就会卡。常见做法是把查询放到QThread里,或者至少加个QApplication::processEvents()让界面喘口气。不过对于课设级项目,数据量不大,先保证功能跑通更重要。
中文乱码分两种:一种是数据库里存进去就是乱码,另一种是界面显示乱码。前者检查 MySQL 的字符集是不是utf8mb4,建库建表时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。后者检查 Qt 源文件编码,Qt Creator 里默认是 UTF-8,但如果你的系统是 GBK,.ui文件里的中文可能显示不正常。在main.cpp里加QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"))可以统一编码,但 Qt 6 里这个类没了,所以还是建议从建库阶段就统一用 UTF-8。
注意:改完数据库字符集后,已经存进去的乱码数据不会自动恢复,需要重新录入或者用 SQL 更新。
5. 避坑清单:从拆包到答辩,这五个坑我替你踩过了
5.1 坑一:.pro.user文件直接打开导致 Kit 错乱
现象:用 Qt Creator 打开工程后,编译按钮是灰的,或者提示“没有可用的 Kit”。原因:源码包里带了outpatients-appointment-system.pro.user,这个文件记录了原作者的 Qt 路径和编译器配置,到你机器上路径对不上就失效了。解决:删掉.pro.user文件,重新用 Qt Creator 打开.pro,让它根据你本地的环境重新生成配置。这一步几乎每次换机器都要做,别嫌麻烦。
5.2 坑二:数据库名和代码里写的不一致
现象:编译通过,运行后登录报“用户名或密码错误”,但数据库里明明有数据。原因:代码里setDatabaseName("hospital_db")写的是某个库名,而你本地建的库叫别的名字,或者根本没建库。解决:全局搜索代码里的数据库名,统一改成你本地实际的库名;同时确认表名和字段名也和代码里的 SQL 一致。常见做法是先把代码里的 SQL 语句复制到 MySQL 客户端里执行一遍,能查到结果再跑程序。
5.3 坑三:MySQL 8.0 认证插件不兼容
现象:QSqlDatabase::open()返回 false,错误信息里带Authentication plugin 'caching_sha2_password' cannot be loaded。原因:MySQL 8.0 默认用caching_sha2_password,而老版 Qt 的 MySQL 驱动只支持mysql_native_password。解决:改用户认证方式,执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';,然后FLUSH PRIVILEGES;。改完再连,基本就能通。
5.4 坑四:界面控件 objectName 被改后代码找不到
现象:程序启动后某个按钮点了没反应,或者直接崩溃。原因:你在 Qt Designer 里改了控件的 objectName,但对应的.cpp里还在用旧名字调ui->oldName。解决:改完.ui后,全局搜索旧 objectName,把所有引用同步改掉。更稳妥的做法是改之前先记下哪些控件在代码里被引用过,只改没被引用的,或者改完立刻编译看报错。
5.5 坑五:预约时间没有做冲突校验
现象:同一个医生同一时间段被多个患者预约成功,数据库里出现重复记录。原因:tiingzhen.cpp里写入预约记录前没有查重,直接 insert。解决:在插入前先SELECT COUNT(*) FROM appointment WHERE doctor_id = ? AND time = ?,如果大于 0 就提示“该时段已被预约”。这个校验加在业务层比加在数据库唯一索引更灵活,因为你可以给用户更友好的提示。
6. 在现有代码上加功能:从“能跑”到“能改”的进阶手法
6.1 加一个“取消预约”功能需要动哪几个文件
取消预约的本质是把appointment表里某条记录的status从booked改成cancelled,或者在界面上直接删除记录。我一般会先在患者主窗口加一个“我的预约”列表,用QTableWidget展示当前患者的预约记录,每行加一个“取消”按钮。按钮的槽函数里拿到预约 ID,执行 UPDATE 语句。
// 在 patient.cpp 里加取消预约的槽函数 void Patient::on_cancelButton_clicked() { int row = ui->appointmentTable->currentRow(); if (row < 0) { QMessageBox::information(this, "提示", "请先选中一条预约记录"); return; } int appointmentId = ui->appointmentTable->item(row, 0)->text().toInt(); QSqlQuery query; query.prepare("UPDATE appointment SET status = 'cancelled' WHERE id = ?"); query.addBindValue(appointmentId); if (query.exec()) { QMessageBox::information(this, "成功", "预约已取消"); refreshAppointmentTable(); // 刷新列表 } else { QMessageBox::warning(this, "失败", "取消失败,请重试"); } }这里currentRow()拿到用户选中的行号,再从第一列取预约 ID。refreshAppointmentTable()是你自己写的刷新函数,重新查一遍数据库把最新状态显示出来。参数说明:appointmentTable是QTableWidget的 objectName,第一列存的是预约 ID,这个列可以隐藏起来不显示给用户,但数据要存进去。
6.2 用 QSS 快速改界面风格,不用动 .ui 文件
如果你觉得默认的灰色界面太“课设风”,可以用 QSS 给控件换颜色和圆角。在main.cpp里读一个.qss文件,或者直接在代码里写样式字符串。常见做法是给按钮加圆角和悬停变色,给输入框加边框。
// 在 main.cpp 里加载 QSS 样式 QFile styleFile(":/style.qss"); if (styleFile.open(QFile::ReadOnly)) { QString style = QLatin1String(styleFile.readAll()); qApp->setStyleSheet(style); }对应的style.qss内容示例:
QPushButton { background-color: #4a90d9; color: white; border-radius: 4px; padding: 6px 16px; } QPushButton:hover { background-color: #357abd; } QLineEdit { border: 1px solid #ccc; border-radius: 3px; padding: 4px; }QSS 的好处是不用改.ui文件,也不影响逻辑代码,改完重新运行就能看到效果。注意资源文件路径:如果 QSS 放在qrc资源里,用:/style.qss;如果放在可执行文件旁边,用相对路径style.qss。
6.3 答辩前必做的三项验证
第一项:把数据库导出成.sql文件,换一台机器导入后重新编译运行,确认不依赖你本地的特殊配置。第二项:把所有窗口的跳转路径走一遍,特别是登录失败、注册重复用户名、预约时间冲突这些异常分支,确保不会崩溃。第三项:检查代码里有没有硬编码的绝对路径,比如C:/Users/xxx/...,有的话改成相对路径或资源路径。
从那以后我每次拿到别人的 Qt 工程,都强制先删.pro.user、再对数据库名、最后跑一遍全流程异常分支,这三步走完基本不会在演示时翻车。希望帮到你。
本文还有配套的精品资源,点击获取