news 2026/10/22 13:48:14

Qt+MySQL门诊预约系统源码实战:从环境搭建到功能扩展

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt+MySQL门诊预约系统源码实战:从环境搭建到功能扩展

简介:本资源为基于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 保持一致。

表名关键字段用途代码中对应查询
userid, username, password, role登录与角色判断login.cpp 登录校验
departmentid, name科室列表展示choosedepartment.cpp 查询
doctorid, name, dept_id, schedule医生信息与排班choosetime.cpp 按科室查医生
appointmentid, 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、再对数据库名、最后跑一遍全流程异常分支,这三步走完基本不会在演示时翻车。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 13:47:06

豆包AI助手实战指南:从对话机器人到智能生活多面手

豆包AI助手火了这么长时间&#xff0c;我身边不少朋友都在用&#xff0c;但真正把它用成“智能生活多面手”的人其实不多。多数人还停留在随手问两句、让它写个小作文的阶段&#xff0c;实际上豆包能干的事情远不止这些——从日常问答、写作辅助&#xff0c;到行程规划、学习辅…

作者头像 李华
网站建设 2026/10/22 13:46:29

提示工程持续部署:从版本化到灰度回滚的完整策略

最近在搭提示工程持续部署机制的时候&#xff0c;我被问得最多的一个问题特别直接&#xff1a;Prompt也要做持续部署&#xff1f;不都是改完直接上线看效果吗&#xff1f;这个问题的背后&#xff0c;其实是很多团队仍然在用“改代码、跑测试、发版”那套传统CD思路来管理人机交…

作者头像 李华
网站建设 2026/10/22 13:46:02

基于Python与OpenCV的智能交通信号灯动态配时优化系统实现

城市路口那套红绿灯&#xff0c;大多数时候是“固定配时”——不管东边堵成什么样&#xff0c;绿灯该亮几秒还是几秒。明明直行车流已经排到上一个路口了&#xff0c;绿灯还得等几十秒才轮到你。这套基于Python与OpenCV的智能交通信号灯优化系统&#xff0c;就是干这个的&#…

作者头像 李华
网站建设 2026/10/22 13:45:36

Kubernetes生态演进全解析:从容器运行时到GitOps的架构变迁

1. 从“能跑起来”到“跑得稳”&#xff1a;K8s生态的认知拐点 刚接触容器编排那会儿&#xff0c;我以为把应用打包成镜像、写个Deployment、暴露一个Service&#xff0c;就算“掌握Kubernetes”了。后来在生产环境里滚了几年才发现&#xff0c;那顶多叫“会用kubectl”&#x…

作者头像 李华
网站建设 2026/10/22 13:42:58

Linux字符设备驱动开发:设备号申请、cdev注册与设备节点创建详解

关注 Linux 驱动开发的朋友&#xff0c;应该都绕不过“字符设备”这个坎。字符设备驱动是驱动开发里最基础也最高频的类型&#xff0c;从按键、触摸屏到传感器&#xff0c;底层基本都是这套框架。而我写这篇文章的原因很直接&#xff1a;不少刚入门的开发者&#xff0c;写完 he…

作者头像 李华
网站建设 2026/10/22 13:42:47

Git版本控制入门:从工作区到远程仓库的完整提交指南

1. 从"作业交不上"说起&#xff1a;为什么你需要搞懂Git的版本阶段很多人第一次接触Git&#xff0c;都是被逼的——老师要求用Git提交作业&#xff0c;或者团队协作时别人甩给你一个仓库地址让你clone下来干活。结果呢&#xff1f;打开终端&#xff0c;面对一堆命令&…

作者头像 李华