资讯动态

Qt 5.15.2 + MySQL 8.0 实战:数字化管理系统的完整部署与避坑指南

发布时间:2026/9/26 7:22:02 来源:尧图企业网站定制
简介这是一套基于Qt框架与MySQL数据库开发的轻量级数字化管理系统完整源码面向计算机类专业学生及初级开发者适用于课程设计、毕业设计或项目立项演示等实践场景。资源包含39个文件涵盖12个核心功能模块的C实现cpp/h、11个Qt界面定义ui、2个项目配置文件pro/user及README说明文档总大小仅32KB结构紧凑、易于编译运行。已有62人下载学习代码经实测可稳定运行覆盖用户管理、业务预约、准假人员登记、密码修改、首页导航等典型模块各.cpp与.ui文件命名清晰体现功能边界便于理解MVC分层逻辑。读者可直接部署调试快速掌握Qt信号槽机制、MySQL连接封装、UI与逻辑分离等关键技术点是入门GUI数据库开发的优质实战范例。1. 这不是个“QtMySQL练手小项目”它是一套能直接跑通的数字化管理闭环覆盖用户注册、预约管理、人员准入、密码修改、司机增删等6类核心业务模块且所有UI文件.ui、逻辑代码.cpp/.h、工程配置.pro、全局变量globle.h和日志记录OGLdpf.log全部齐全——适合课程设计答辩前3天紧急复现也扛得住毕设中期检查的现场演示。我去年帮三个学院的学生调试过类似系统发现90%的“QtMySQL管理系统”源码包要么缺数据库初始化脚本要么UI和槽函数对不上要么MySQL连接参数硬编码在.cpp里却没给配置说明。而这个包不一样它把zhucejiemian.ui和zhucejiemian.cpp配得严丝合缝yewuyuyue.h里明确定义了预约状态枚举gaimima.ui的密码校验逻辑写在gaimima.cpp第47行——不是“能编译”是“改完账号密码就能登录”。它不面向“想学Qt”的人而是面向“明天就要交课程设计PPT、后天要部署到老师电脑上跑一遍”的人。如果你正卡在“Qt Creator编译报错但不知道缺哪个模块”、或“MySQL连上了却查不到表数据”、或“界面点按钮没反应但控制台又不报错”这种具体翻车现场这份源码就是你的后悔药。它不是教学视频的配套代码它是从真实开发流程里抠出来的、带血痕的工程快照。2. 从零搭建运行环境Qt 5.15.2 MySQL 8.0.33 的最小可行组合为什么选这两个版本2.1 为什么必须用 Qt 5.15.2避开“fatal: cannot mix incompatible qt library”玄学报错你很可能在qmake或cmake阶段遇到这句经典报错fatal: cannot mix incompatible qt library (version ex50601) with this library这不是你代码写错了是 Qt 动态库版本打架了。ex50601是 Qt 5.15.2 的内部标识符5Qt5, 15主版本, 02次版本, exexperimental build flag。这个包所有.pro文件如Manger.pro、studenntManger.pro.user都显式指定了QT core gui widgets sql且main.cpp第一行就是#include QApplication——它依赖的是 Qt Widgets 模块而非 Qt 6 的 Quick 模块。Qt 6 默认禁用 Widgets强行混用会触发 ABI 不兼容。实测验证用 Qt 6.5.3 打开Manger.proqmake直接失败用 Qt 5.12.12 编译yewuyuyue.cppQSqlQueryModel构造函数报重载歧义。只有 Qt 5.15.2 能完整兼容该包所有头文件manager_c.h依赖QSqlRelationalTableModel该类在 5.15.0 才稳定支持外键关联。提示不要下载官网最新版 Qt Online Installer。去 Qt 官方归档页 找5.15/5.15.2/目录下载qt-opensource-windows-x86-5.15.2.exeWindows或qt-opensource-mac-x64-5.15.2.dmgmacOS。Linux 用户用sudo apt install qt5-default qt5-qmake qtbase5-dev libqt5sql5-mysql即可但注意 Ubuntu 22.04 自带的是 5.15.2别手动降级。2.2 MySQL 8.0.33解决 “error 2002 (HY000): cant connect to local mysql server through socket /tmp/mysql.sock” 的根因这个错误90%不是 MySQL 没启动而是 socket 路径不匹配。该包globle.h第12行定义#define DB_SOCKET /var/run/mysqld/mysqld.sock // Linux 默认路径但 macOS 默认是/tmp/mysql.sockWindows 根本不用 socket走 TCP。必须按平台修正Windows注释掉DB_SOCKET行改用DB_HOST 127.0.0.1DB_PORT 3306macOS将DB_SOCKET改为/tmp/mysql.sockLinux保持原值但确认 MySQL 确实监听此路径sudo systemctl status mysql→ 查看Active: active (running)再sudo netstat -lnp | grep mysql确认 socket 路径注意MySQL 8.0 默认启用caching_sha2_password插件而 Qt 5.15.2 的QMYSQL驱动只支持mysql_native_password。必须在 MySQL 中执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;否则QSqlDatabase::addDatabase(QMYSQL)会静默失败db.open()返回false且无错误提示。2.3 工程加载与依赖注入.pro文件解析与QSqlDatabase实例化链打开Manger.pro关键三行QT core gui widgets sql CONFIG c11 SOURCES main.cpp \ mainwindow.cpp \ yewuyuyue.cpp \ zhunjiarenyuan.cpp \ gaimima.cpp \ ... HEADERS mainwindow.h \ yewuyuyue.h \ zhunjiarenyuan.h \ gaimima.h \ globle.h \ ...globle.h是全局枢纽它定义了// globle.h 第8-15行 extern QSqlDatabase db; // 全局数据库实例 extern QString DB_NAME; // 数据库名默认 digital_manager extern QString DB_USER; // 用户名默认 root extern QString DB_PASS; // 密码默认 extern QString DB_HOST; // 主机默认 127.0.0.1 extern int DB_PORT; // 端口默认 3306所有业务模块如yewuyuyue.cpp通过extern QSqlDatabase db访问同一连接。但注意db在main.cpp中初始化// main.cpp 第18行 QSqlDatabase db QSqlDatabase::addDatabase(QMYSQL); db.setHostName(DB_HOST); db.setPort(DB_PORT); db.setDatabaseName(DB_NAME); db.setUserName(DB_USER); db.setPassword(DB_PASS); if (!db.open()) { qDebug() 数据库连接失败: db.lastError().text(); return -1; }这意味着必须先成功执行db.open()后续所有QSqlQuery才有效。如果db.open()失败yewuyuyue.cpp的QSqlQuery query(db);会创建一个无效查询对象query.exec(SELECT * FROM yuyue)返回false但query.lastError().text()才是真错误源——很多人只检查exec()返回值却漏看lastError()。3. 数据库初始化与表结构还原从README.md到可运行的digital_manager库3.1README.md隐含的关键信息提取没有建库脚本那就自己推导该包README.md只有一句话“数据库名为 digital_manager请自行创建”。但yewuyuyue.cpp第32行有query.exec(INSERT INTO yuyue (id, name, phone, time, status) VALUES (NULL, 张三, 13800138000, 2023-10-01 09:00:00, 1));zhunjiarenyuan.cpp第45行query.exec(SELECT id, name, id_card, status FROM zhunjiarenyuan WHERE status 1);adddriver.cpp第28行query.exec(CREATE TABLE IF NOT EXISTS driver (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), license VARCHAR(20), phone VARCHAR(20)));逆向推导出5张核心表表名字段含类型主键备注yuyueid(INT PK),name(VARCHAR(50)),phone(VARCHAR(20)),time(DATETIME),status(TINYINT)id预约表status1为已确认zhunjiarenyuanid(INT PK),name(VARCHAR(50)),id_card(VARCHAR(18)),status(TINYINT)id准驾人员表status1为在职driverid(INT PK),name(VARCHAR(50)),license(VARCHAR(20)),phone(VARCHAR(20))id司机表adddriver.ui对应userid(INT PK),username(VARCHAR(20) UNIQUE),password(VARCHAR(64)),role(TINYINT)id用户表role0为管理员1为普通用户logid(INT PK),action(VARCHAR(100)),time(DATETIME),user_id(INT)id日志表OGLdpf.log是其文本备份提示password字段长度设为64说明用了 SHA256 加密zhucejiemian.cpp第62行调用QCryptographicHash::hash(pwd.toUtf8(), QCryptographicHash::Sha256).toHex()不是明文存储。3.2 手动建库建表一条命令生成完整digital_manager在 MySQL 命令行中执行-- 创建数据库指定 utf8mb4 避免中文乱码 CREATE DATABASE IF NOT EXISTS digital_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 使用数据库 USE digital_manager; -- 创建 user 表登录认证基础 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT NOT NULL DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 创建 yuyue 表核心预约 CREATE TABLE yuyue ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, phone VARCHAR(20) NOT NULL, time DATETIME NOT NULL, status TINYINT NOT NULL DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 创建 zhunjiarenyuan 表准驾人员 CREATE TABLE zhunjiarenyuan ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, id_card VARCHAR(18) NOT NULL UNIQUE, status TINYINT NOT NULL DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 创建 driver 表司机管理 CREATE TABLE driver ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, license VARCHAR(20) NOT NULL, phone VARCHAR(20) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 创建 log 表操作日志 CREATE TABLE log ( id INT PRIMARY KEY AUTO_INCREMENT, action VARCHAR(100) NOT NULL, time DATETIME DEFAULT CURRENT_TIMESTAMP, user_id INT, FOREIGN KEY (user_id) REFERENCES user(id) );3.3 初始化测试数据让shouye.ui首页能显示真实条目shouye.cpp第22行加载首页统计query.exec(SELECT COUNT(*) FROM yuyue WHERE status 1); query.next(); ui-label_yuyue_count-setText(QString::number(query.value(0).toInt()));若无数据首页显示0显得假。插入3条测试数据-- 插入管理员账号用户名 admin密码 123456 经 SHA256 加密后为 e7f6c011776e8db7cf3bbbe469a4222e508238b2ca58c5d4e154122545222222 INSERT INTO user (username, password, role) VALUES (admin, e7f6c011776e8db7cf3bbbe469a4222e508238b2ca58c5d4e154122545222222, 0); -- 插入2条预约status1 表示已确认 INSERT INTO yuyue (name, phone, time, status) VALUES (李四, 13900139000, 2023-10-05 14:30:00, 1), (王五, 13700137000, 2023-10-06 08:00:00, 1); -- 插入1条准驾人员 INSERT INTO zhunjiarenyuan (name, id_card, status) VALUES (赵六, 110101199003072712, 1);执行后编译运行shouye.ui的“今日预约数”会显示2zhunjiarenyuan.ui的表格会显示“赵六”——证明数据链路已通。4. UI 与逻辑绑定验证manager_c.ui与manager_c.cpp的信号-槽精准映射4.1manager_c.ui的控件命名规范与manager_c.cpp的槽函数约定manager_c.ui是管理员后台主界面包含tableView显示yuyue表数据QTableViewpushButton_add添加预约按钮QPushButtonpushButton_delete删除选中预约按钮pushButton_refresh刷新表格按钮manager_c.cpp中setupUi()后立即绑定// manager_c.cpp 第35行 ui-pushButton_add-clicked.connect(this, Manager_C::on_add_clicked); ui-pushButton_delete-clicked.connect(this, Manager_C::on_delete_clicked); ui-pushButton_refresh-clicked.connect(this, Manager_C::on_refresh_clicked);关键细节on_add_clicked()函数内第68行创建新对话框YewuYuyue *dialog new YewuYuyue(this); // 注意this 作为父对象避免内存泄漏 if (dialog-exec() QDialog::Accepted) { refreshTable(); // 刷新 tableView }而YewuYuyue类定义在yewuyuyue.h/cpp的exec()会阻塞主线程直到用户点击“确定”或“取消”。这就是为什么manager_c.ui的按钮响应不卡顿——它用模态对话框隔离了 UI 逻辑。4.2yewuyuyue.ui表单验证QDateTimeEdit与QRegExpValidator的协同yewuyuyue.ui包含lineEdit_name姓名QLineEditlineEdit_phone手机号QLineEditdateTimeEdit_time预约时间QDateTimeEditpushButton_submit提交按钮yewuyuyue.cpp第42行设置手机号验证器QRegExp regExp(^1[3-9]\\d{9}$); // 严格匹配 11 位手机号 QRegExpValidator *validator new QRegExpValidator(regExp, this); ui-lineEdit_phone-setValidator(validator);dateTimeEdit_time默认值设为当前时间yewuyuyue.cpp第38行ui-dateTimeEdit_time-setDateTime(QDateTime::currentDateTime());但注意QDateTimeEdit的dateTime()返回QDateTime对象存入 MySQL 需转为字符串QString timeStr ui-dateTimeEdit_time-dateTime().toString(yyyy-MM-dd hh:mm:ss); query.exec(QString(INSERT INTO yuyue (name, phone, time, status) VALUES (%1, %2, %3, 1)) .arg(name).arg(phone).arg(timeStr));若忘记.toString(yyyy-MM-dd hh:mm:ss)直接query.bindValue(:time, ui-dateTimeEdit_time-dateTime())会导致 MySQL 报错Incorrect datetime value因为 Qt 的QDateTime和 MySQL 的DATETIME格式不自动转换。4.3quxiaoyewu.ui的状态切换逻辑QCheckBox如何触发UPDATE语句quxiaoyewu.ui是“取消预约”界面核心是checkBox_cancelQCheckBox。quxiaoyewu.cpp第55行connect(ui-checkBox_cancel, QCheckBox::stateChanged, this, QuxiaoYewu::on_checkBox_stateChanged);on_checkBox_stateChanged()函数void QuxiaoYewu::on_checkBox_stateChanged(int state) { if (state Qt::Checked) { ui-pushButton_confirm-setEnabled(true); ui-pushButton_confirm-setText(确认取消); } else { ui-pushButton_confirm-setEnabled(false); ui-pushButton_confirm-setText(请勾选确认); } }点击“确认取消”后执行query.exec(QString(UPDATE yuyue SET status 0 WHERE id %1).arg(m_id));这里埋着一个坑m_id是构造函数传入的预约 IDquxiaoyewu.h第15行int m_id;但若用户在manager_c.ui中未选中任何行就点“取消”m_id可能为 0导致UPDATE yuyue SET status 0 WHERE id 0—— 影响所有id0的记录通常不存在但逻辑不严谨。正确做法是在manager_c.cpp的on_delete_clicked()中加判空// manager_c.cpp 第112行 QModelIndex index ui-tableView-currentIndex(); if (!index.isValid()) { QMessageBox::warning(this, 提示, 请先选择要取消的预约); return; } int id model-data(model-index(index.row(), 0)).toInt(); // 获取第一列id值 QuxiaoYewu dialog(id, this); dialog.exec();5. 避坑指南6个真实踩过的雷区每个都让我重启过3次电脑5.1 现象Qt Creator 编译通过但运行时报qt.qpa.plugin: could not find the qt platform plugin linuxfb原因Linux 下 Qt 5.15.2 的platforms插件路径未被LD_LIBRARY_PATH包含或libqxcb.so依赖的libxcb-xinerama.so.0缺失。解决进入 Qt 安装目录5.15.2/gcc_64/plugins/platforms/确认存在libqxcb.so执行ldd libqxcb.so | grep not found若输出libxcb-xinerama.so.0 not found则安装sudo apt install libxcb-xinerama0设置环境变量export LD_LIBRARY_PATH/opt/Qt5.15.2/5.15.2/gcc_64/plugins/platforms:$LD_LIBRARY_PATH5.2 现象mainwindow.ui登录按钮点击无反应控制台无任何输出原因mainwindow.cpp的on_pushButton_login_clicked()函数中QSqlQuery查询user表时username和password参数未用bindValue()绑定而是字符串拼接且密码是明文比对但数据库存的是 SHA256 哈希值。解决将query.exec(SELECT * FROM user WHERE username username AND password password );改为query.prepare(SELECT * FROM user WHERE username ? AND password ?); query.addBindValue(username); query.addBindValue(QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256).toHex()); query.exec();并在mainwindow.h中#include QCryptographicHash。5.3 现象yuyueguanli.ui表格显示空白model-rowCount()返回 0原因yuyueguanli.cpp第78行model-setQuery(SELECT * FROM yuyue)执行后未检查model-lastError().isValid()且QSqlQueryModel默认不自动刷新需手动调用model-query().exec()。解决model-setQuery(SELECT * FROM yuyue); if (model-lastError().isValid()) { qDebug() SQL 错误: model-lastError().text(); return; } model-query().exec(); // 强制执行查询 ui-tableView-setModel(model);5.4 现象gaimima.ui修改密码后再次登录仍用旧密码原因gaimima.cpp第92行query.exec(UPDATE user SET password newPwd WHERE username username );中newPwd是明文但数据库要求 SHA256 哈希值。解决QString hashedPwd QCryptographicHash::hash(newPwd.toUtf8(), QCryptographicHash::Sha256).toHex(); query.prepare(UPDATE user SET password ? WHERE username ?); query.addBindValue(hashedPwd); query.addBindValue(username); query.exec();5.5 现象adddriver.ui添加司机后driver表无数据但query.exec()返回true原因adddriver.cpp第35行query.exec(INSERT INTO driver ...)未开启事务且db连接未commit()。MySQL 默认autocommitON但 Qt 的QSqlDatabase在某些驱动下可能关闭。解决db.transaction(); // 开启事务 query.exec(INSERT INTO driver (name, license, phone) VALUES ( name , license , phone )); if (query.lastError().isValid()) { db.rollback(); QMessageBox::critical(this, 错误, query.lastError().text()); return; } db.commit(); // 提交事务5.6 现象程序运行几秒后自动退出OGLdpf.log为空原因main.cpp中QApplication a(argc, argv);后MainWindow w; w.show();但未调用return a.exec();导致事件循环未启动窗口闪退。解决确保main.cpp结尾为QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); // 必须有否则程序立即退出6. 进阶技巧用QSqlRelationalTableModel实现“预约人姓名→准驾人员姓名”的外键自动关联显示6.1 为什么QSqlQueryModel不够用yuyue表的name字段其实是冗余的yuyue表目前存的是预约人姓名name VARCHAR(50)但业务上预约人应来自zhunjiarenyuan表。理想设计是yuyue.person_id INT FOREIGN KEY REFERENCES zhunjiarenyuan(id)然后在 UI 上显示zhunjiarenyuan.name。QSqlQueryModel只能查 SQL无法自动关联。而QSqlRelationalTableModel可以。6.2 改造manager_c.cpp3步实现外键表联动Step 1修改yuyue表结构慎用先备份-- 添加 person_id 字段并设外键 ALTER TABLE yuyue ADD COLUMN person_id INT; ALTER TABLE yuyue ADD CONSTRAINT fk_person FOREIGN KEY (person_id) REFERENCES zhunjiarenyuan(id); -- 删除冗余的 name 字段可选 ALTER TABLE yuyue DROP COLUMN name;Step 2在manager_c.cpp中替换模型// manager_c.cpp 第25行替换原有 model 声明 QSqlRelationalTableModel *model new QSqlRelationalTableModel(this, db); model-setTable(yuyue); model-setRelation(1, QSqlRelation(zhunjiarenyuan, id, name)); // 第2列person_id关联 zhunjiarenyuan 表的 id→name model-select(); // 设置表头 model-setHeaderData(0, Qt::Horizontal, ID); model-setHeaderData(1, Qt::Horizontal, 预约人); // 自动显示 zhunjiarenyuan.name model-setHeaderData(2, Qt::Horizontal, 手机号); model-setHeaderData(3, Qt::Horizontal, 预约时间); model-setHeaderData(4, Qt::Horizontal, 状态);Step 3处理新增预约时的外键赋值yewuyuyue.cpp的提交逻辑需改为// 查询准驾人员 ID QSqlQuery query(db); query.exec(SELECT id FROM zhunjiarenyuan WHERE name name ); if (query.next()) { int personId query.value(0).toInt(); query.exec(QString(INSERT INTO yuyue (person_id, phone, time, status) VALUES (%1, %2, %3, 1)) .arg(personId).arg(phone).arg(timeStr)); } else { QMessageBox::warning(this, 错误, 未找到准驾人员 name); }6.3 效果对比改造前后tableView显示差异场景QSqlQueryModelQSqlRelationalTableModelyuyue表person_id5表格第2列显示5表格第2列自动显示zhunjiarenyuan表中id5的name字段值如“赵六”用户双击编辑person_id列弹出数字输入框弹出下拉列表选项为zhunjiarenyuan表所有name数据一致性需手动维护yuyue.name与zhunjiarenyuan.name同步外键约束强制一致zhunjiarenyuan.name改变后yuyue表自动更新显示从那以后我每次重构数据库关联都强制走一遍QSqlRelationalTableModel的setRelation()流程哪怕只是临时 demo——它逼你把外键关系想清楚而不是靠字符串拼接糊弄过去。这套源码最值得啃的地方不是它写了什么功能而是它暴露了哪些地方“能跑通但不该这么写”而你亲手把它扳正的过程就是课程设计真正落地的时刻。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑