ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Qt6+MySQL实战:从零构建C++桌面用户信息管理系统

Qt6+MySQL实战:从零构建C++桌面用户信息管理系统 先说结论这是非常适合 Qt 初学者用来打通“C 界面开发 - 数据库操作 - 完整功能闭环”的第一个实战项目。技术栈很稳就是 Qt6 MySQL一个负责界面和交互逻辑一个负责数据落地和查询管理。项目本身不靠花哨概念取胜核心价值在于让你亲手把一个桌面应用从 0 搭到能录入、能查询、能修改、能删除。这篇文章不会只给一个“项目介绍”而是把整个实战过程拆开讲环境怎么配、数据库怎么建、Qt 工程怎么写 SQL 模块、增删改查怎么做、遇到连不上数据库和中文乱码时怎么排查。如果你正处在“学会了 C 语法但不知道能做什么”的阶段这个项目值得花一个周末完整做一遍。1. 核心能力速览能力项说明项目类型Qt6 桌面应用 MySQL 数据库实战项目技术栈C / Qt6 / MySQL / QtSql 模块核心功能用户信息管理、数据新增、数据修改、数据删除、数据查询适合人群掌握 C 基础语法、想接触 GUI 开发和数据库开发的初学者运行环境Windows 或 Linux 均可需要安装 Qt6 和 MySQL 服务IDE 选择Qt Creator、Visual Studio Qt 插件、VS Code CMake 均可数据库MySQL 5.7 / 8.x注意驱动插件 QMYSQL 是否可用扩展能力可扩展登录验证、表格分页、多条件查询、数据统计报表学习收益掌握 Qt 信号槽、界面布局、QSqlDatabase、QSqlQuery、事务处理从功能上看这个项目几乎没有“高不可攀”的门槛。它比控制台程序多了一个界面层比单纯学数据库多了一套可交互的入口对新手来说刚好卡在“有点挑战但能完成”的位置。2. 为什么选“Qt6 MySQL”作为第一个实战项目很多人学完 C 语法之后会有一种感觉指针、类、继承、多态都学过了但写出来的东西都在黑色控制台里没有“像个软件”的体验。Qt 的价值就在这里——它把 C 和图形界面连接起来让你能用熟悉的语言做出现实中桌面软件的界面。MySQL 的选择同理。它是目前使用最广泛的开源关系型数据库之一网上教程多、问题反馈也多遇到报错很容易找到解决方案。Qt 对 MySQL 的支持通过 QtSql 模块完成底层的驱动是 QMYSQL。这意味着你不需要额外引入笨重的数据库客户端库只需要让 Qt 能找到对应的驱动插件就能在代码里直接执行 SQL 语句。这个项目的典型使用场景包括做一个本地用户信息登记工具记录姓名、手机号、邮箱、年龄等数据做一个简单图书管理或商品库存管理系统练习表设计和增删改查作为毕业设计或课程设计的基础框架后续向登录、权限、报表方向扩展。需要注意边界Qt6 的官方预编译包里QMYSQL 驱动插件并不总是默认附带。不同操作系统、不同编译套件下能不能直接用“QMYSQL”这个驱动名是第一步要验证的事。这个问题如果你提前知道就能少走很多弯路。3. 开发环境准备3.1 安装 Qt6如果你没有安装过 Qt建议从 Qt 官网下载安装包选择 Qt 在线安装器。安装时需要注意勾选组件Qt 6.x 的主组件对应编译器套件的 MinGW 或 MSVC 模块Qt Creator IDE通常默认勾选如果你的环境中没有编译器还需要单独安装 MinGW 或 Visual Studio Build Tools。安装 Qt 时不需要全选所有模块勾选 Qt Widgets、Qt Sql 和 CMake 相关工具就够用。磁盘空间可以预留 15GB 左右实际占用取决于你选择的组件数量。3.2 安装 MySQL 数据库Windows 下推荐使用 MySQL Installer 安装 MySQL 8.x。安装过程中需要注意Server 类型选 Development Machine 或 Server Machine 都可以本地学习选默认即可认证方式建议选 Legacy Authentication8.0 默认是 caching_sha2_password很多客户端驱动会因为认证协议不匹配报错这个后面会讲设置 root 密码时不要使用过于简单的密码并且记录下来后面 Qt 连接要用端口默认 3306如果本机已有其他数据库占用需要改成其他端口并记住。安装完成后可以在命令行里验证 MySQL 服务状态mysql -u root -p输入密码后能进入mysql提示符说明数据库服务正常。3.3 安装 MySQL 图形管理工具新手建议配合 MySQL Workbench 或 Navicat 使用。图形工具的主要作用是先手工建库、建表、插入测试数据确认 SQL 本身没有问题再回到 Qt 里调用。这样可以减少“代码问题还是 SQL 问题”的混淆。3.4 准备编译器与 CMakeQt6 从创建工程到编译官方推荐的构建系统是 CMake。你不需要先精通 CMake只需要了解最基本的配置即可让 Qt 自动处理AUTOMOC、AUTOUIC等生成过程通过find_package(Qt6 COMPONENTS Core Gui Widgets Sql)找到需要的模块在target_link_libraries里链接 Qt6::Sql。如果你在 Qt Creator 里新建 Qt Widgets Application默认的 CMakeLists.txt 已经包含大部分基础配置你只需要手动补上 Sql 模块。4. 数据库设计与初始化4.1 新建数据库打开 MySQL Workbench 或命令行创建一个专门给 Qt 项目用的数据库。CREATE DATABASE IF NOT EXISTS qt_demo DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE qt_demo;使用utf8mb4字符集是避免中文乱码的关键一步。MySQL 默认字符集在不同版本上可能不同如果你的表不是 utf8mb4插入中文后 Qt 界面显示会出现问号或乱码。4.2 建用户信息表CREATE TABLE IF NOT EXISTS users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(64) NOT NULL, email VARCHAR(128), age INT UNSIGNED DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB;这张表包含四个核心字段自增主键 id、用户名 username、邮箱 email、年龄 age。字段不用多能跑通增删改查即可。后续可以继续加手机号、地址、备注等字段。4.3 插入测试数据INSERT INTO users (username, email, age) VALUES (zhangsan, zhangsanexample.com, 25), (lisi, lisiexample.com, 30), (wangwu, wangwuexample.com, 22);插入成功后用SELECT * FROM users;查看数据确认中文字段和数字字段都没有异常。4.4 MySQL 驱动与认证协议检查Qt 的 QMYSQL 驱动通过 MySQL 客户端库与 MySQL Server 通信。如果你在这个阶段就遇到Authentication plugin caching_sha2_password cannot be loaded一类的错误说明 MySQL 8 默认的认证协议和当前客户端库不兼容。两种常见处理方式安装 MySQL 时将默认认证方式选为 Legacy Authenticationmysql_native_password创建专用账号时指定mysql_native_password认证方式。这一条很多 PHP、Python 连接 MySQL 的教程里也有提到属于跨语言的通用坑。提前在数据库侧处理完Qt 端才不会被这种底层问题卡住。5. 创建 Qt 工程并完成界面设计5.1 新建工程在 Qt Creator 中选择File - New Project - Application (Qt Widgets Application)。项目名称建议取UserManager构建系统选择 CMake。工程创建完成后默认生成以下文件CMakeLists.txtmain.cppmainwindow.hmainwindow.cppmainwindow.ui5.2 修改 CMakeLists.txt 添加 Sql 模块cmake_minimum_required(VERSION 3.16) project(UserManager VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Sql) qt_add_executable(UserManager main.cpp mainwindow.cpp mainwindow.h mainwindow.ui ) target_link_libraries(UserManager PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Sql )关键点是find_package中加入了Sql组件target_link_libraries中加入了Qt6::Sql。不加这两项编译时会报找不到QSqlDatabase头文件或链接错误。5.3 设计主界面用mainwindow.ui打开 Qt Designer放置以下控件QLineEdit用户名输入框、年龄输入框、邮箱输入框QPushButton添加按钮、查询按钮、修改按钮、删除按钮QTableWidget或QTableView显示查询结果一个“刷新所有”按钮用于重新加载全表数据。界面布局建议先放一个表单区再放一个按钮区最下面放表格控件。新手最容易犯的错是把所有控件堆在一个布局里拉伸窗口后控件不会自适应。可以使用QVBoxLayout纵向布局内嵌QHBoxLayout放输入框和按钮这样窗口缩放时页面不会乱。5.4 窗口尺寸与初始状态MainWindow 构造函数中设置窗口标题和初始尺寸MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ui-setupUi(this); setWindowTitle(QStringLiteral(用户信息管理 - Qt6 MySQL)); resize(800, 500); ui-tableWidget-setColumnCount(5); QStringList headers; headers QStringLiteral(ID) QStringLiteral(用户名) QStringLiteral(邮箱) QStringLiteral(年龄) QStringLiteral(创建时间); ui-tableWidget-setHorizontalHeaderLabels(headers); }6. Qt 连接 MySQL 与增删改查核心代码6.1 建立数据库连接连接数据库的代码建议放到一个独立的函数中启动时调用一次。Qt 使用QSqlDatabase管理数据库连接同一个连接名不能重复添加所以先判断是否已经包含该连接。#include QSqlDatabase #include QSqlQuery #include QSqlError #include QDebug bool MainWindow::connectDatabase() { const QString connectionName QStringLiteral(qt_mysql_connection); if (QSqlDatabase::contains(connectionName)) { db QSqlDatabase::database(connectionName); } else { db QSqlDatabase::addDatabase(QStringLiteral(QMYSQL), connectionName); } db.setHostName(QStringLiteral(127.0.0.1)); db.setPort(3306); db.setDatabaseName(QStringLiteral(qt_demo)); db.setUserName(QStringLiteral(root)); db.setPassword(QStringLiteral(你的数据库密码)); if (!db.open()) { qDebug() QStringLiteral(数据库连接失败) db.lastError().text(); return false; } qDebug() QStringLiteral(数据库连接成功); return true; }使用独立连接名是避免多个窗口共用默认连接时产生冲突的常用做法。如果你只在 MainWindow 中使用数据库也可以直接用默认连接名。6.2 查询数据并填充表格查询全部用户并显示到QTableWidget中是每个界面功能的地基。封装成一个refreshTable()函数添加、删除、修改完成后都调用它。void MainWindow::refreshTable() { QSqlQuery query(db); if (!query.exec(QStringLiteral(SELECT id, username, email, age, created_at FROM users ORDER BY id))) { qDebug() QStringLiteral(查询失败) query.lastError().text(); return; } ui-tableWidget-setRowCount(0); int row 0; while (query.next()) { ui-tableWidget-insertRow(row); ui-tableWidget-setItem(row, 0, new QTableWidgetItem(query.value(0).toString())); ui-tableWidget-setItem(row, 1, new QTableWidgetItem(query.value(1).toString())); ui-tableWidget-setItem(row, 2, new QTableWidgetItem(query.value(2).toString())); ui-tableWidget-setItem(row, 3, new QTableWidgetItem(query.value(3).toString())); ui-tableWidget-setItem(row, 4, new QTableWidgetItem(query.value(4).toString())); row; } }这里使用query.next()逐行读取结果集。无论SELECT操作来自什么条件读取方式都是先exec()再通过value(index)按字段顺序取值。6.3 添加用户void MainWindow::on_addButton_clicked() { QString username ui-usernameEdit-text().trimmed(); QString email ui-emailEdit-text().trimmed(); int age ui-ageSpinBox-value(); if (username.isEmpty()) { qWarning() QStringLiteral(用户名不能为空); return; } QSqlQuery query(db); query.prepare(QStringLiteral(INSERT INTO users (username, email, age) VALUES (?, ?, ?))); query.addBindValue(username); query.addBindValue(email); query.addBindValue(age); if (!query.exec()) { qDebug() QStringLiteral(插入失败) query.lastError().text(); return; } refreshTable(); }使用prepare addBindValue而不是直接拼接字符串是为了避免 SQL 注入和中文引号问题。这个习惯应该在第一个项目里就养成。6.4 修改用户修改时需要知道针对哪一行数据操作。常见做法是取表格选中行的第一列 ID。void MainWindow::on_updateButton_clicked() { int row ui-tableWidget-currentRow(); if (row 0) { qWarning() QStringLiteral(请先选择要修改的行); return; } int id ui-tableWidget-item(row, 0)-text().toInt(); QString username ui-usernameEdit-text().trimmed(); QString email ui-emailEdit-text().trimmed(); int age ui-ageSpinBox-value(); QSqlQuery query(db); query.prepare(QStringLiteral(UPDATE users SET username ?, email ?, age ? WHERE id ?)); query.addBindValue(username); query.addBindValue(email); query.addBindValue(age); query.addBindValue(id); if (!query.exec()) { qDebug() QStringLiteral(更新失败) query.lastError().text(); return; } refreshTable(); }6.5 删除用户删除操作涉及数据不可恢复建议写删除前确认。最简单的方式是QMessageBox::question点“是”才执行删除。void MainWindow::on_deleteButton_clicked() { int row ui-tableWidget-currentRow(); if (row 0) { qWarning() QStringLiteral(请先选择要删除的行); return; } int id ui-tableWidget-item(row, 0)-text().toInt(); QString username ui-tableWidget-item(row, 1)-text(); QMessageBox::StandardButton reply; reply QMessageBox::question(this, QStringLiteral(确认删除), QStringLiteral(确定删除用户 %1 吗).arg(username), QMessageBox::Yes | QMessageBox::No); if (reply ! QMessageBox::Yes) { return; } QSqlQuery query(db); query.prepare(QStringLiteral(DELETE FROM users WHERE id ?)); query.addBindValue(id); if (!query.exec()) { qDebug() QStringLiteral(删除失败) query.lastError().text(); return; } refreshTable(); }6.6 条件查询按用户名模糊查询可以更直观地展示 SQL 条件查询在 Qt 里的写法void MainWindow::on_searchButton_clicked() { QString keyword ui-searchEdit-text().trimmed(); QString sql QStringLiteral(SELECT id, username, email, age, created_at FROM users WHERE username LIKE ? ORDER BY id); QSqlQuery query(db); query.prepare(sql); query.addBindValue(QStringLiteral(%%1%).arg(keyword)); if (!query.exec()) { qDebug() QStringLiteral(搜索失败) query.lastError().text(); return; } ui-tableWidget-setRowCount(0); int row 0; while (query.next()) { ui-tableWidget-insertRow(row); ui-tableWidget-setItem(row, 0, new QTableWidgetItem(query.value(0).toString())); ui-tableWidget-setItem(row, 1, new QTableWidgetItem(query.value(1).toString())); ui-tableWidget-setItem(row, 2, new QTableWidgetItem(query.value(2).toString())); ui-tableWidget-setItem(row, 3, new QTableWidgetItem(query.value(3).toString())); ui-tableWidget-setItem(row, 4, new QTableWidgetItem(query.value(4).toString())); row; } }6.7 事务处理如果一次操作涉及多条 SQL 语句比如“把 A 用户的余额减 100加给 B 用户”就必须使用事务。Qt 的写法是bool transferMoney(int fromId, int toId, double amount) { QSqlDatabase db QSqlDatabase::database(); if (!db.transaction()) { return false; } QSqlQuery query(db); query.prepare(QStringLiteral(UPDATE accounts SET balance balance - ? WHERE id ?)); query.addBindValue(amount); query.addBindValue(fromId); bool updateOk1 query.exec(); query.prepare(QStringLiteral(UPDATE accounts SET balance balance ? WHERE id ?)); query.addBindValue(amount); query.addBindValue(toId); bool updateOk2 query.exec(); if (updateOk1 updateOk2) { return db.commit(); } else { db.rollback(); return false; } }事务的关键是任何一条语句失败时都要rollback()否则会出现部分数据被修改的情况。这个知识点在数据库面试题里很常见也是项目里值得特意写出来的亮点。7. 功能测试与效果验证7.1 测试启动与连接运行程序后观察 Qt Creator 的输出窗口。正常情况会看到类似数据库连接成功的日志。如果输出数据库连接失败和错误信息说明问题出在驱动、网络或账号密码先不要继续测界面功能。7.2 添加功能测试在用户名输入框填入“测试用户”邮箱填入testexample.com年龄选择 26点击“添加”按钮。判断标准表格中最下方多出一条记录记录 ID 是数据库自动生成的打开 MySQL Workbench 执行SELECT * FROM users;也能看到同一条数据。7.3 查询功能测试在搜索框输入“zhang”点击搜索。预期结果是表格只显示 username 包含“zhang”的记录。如果搜索无反应优先检查LIKE ?的参数是否带了%通配符。7.4 修改功能测试选中表格中的一行在输入框中把用户名改成新值点击“修改”。判断标准表格中该行数据立即刷新数据库中对应记录同步改变。7.5 删除功能测试选中某行点击“删除”确认弹窗点“是”。判断标准表格中该行消失数据库中数据也已删除。7.6 中文乱码测试添加一条中文用户名“张三”刷新表格。如果显示正常说明数据库字符集、表字符集、Qt 输入输出链路都没有问题。如果显示乱码优先排查是否建表时使用了非 utf8mb4 的字符集以及 MySQL 连接字符集是否正确。8. Qt 连接 MySQL 常见问题与排查方法问题现象可能原因排查方式解决方案启动报Driver not loadedQt 缺少 QMYSQL 驱动插件查看 Qt 安装目录 plugins/sqldrivers 下是否有 qsqlmysql 插件安装对应 MySQL 客户端库或将缺少的插件补充到编译输出目录连接报Access denied for user用户名或密码错误用命令行 mysql -u root -p 测试同一账号确认密码或新建专用数据库账号连接报Unknown database数据库名不对检查 MySQL 中是否存在对应库把qt_demo改成实际库名或先建库连接报Authentication plugin caching_sha2_password cannot be loadedMySQL 8 默认认证协议与客户端不兼容检查 MySQL 用户认证插件修改用户认证方式为 mysql_native_password界面中文乱码数据库字符集不是 utf8mb4检查库和表的字符集重建表或修改字符集插入数据后表格不刷新未调用 refreshTable在按钮槽函数末尾添加断点操作成功后统一调用 refreshTable端口冲突导致连不上3306 被占用或服务未启动命令行执行 netstat -anofindstr 3306编译报找不到QSqlDatabaseCMake 未链接 Qt6::Sql检查 CMakeLists.txt在 find_package 中加入 Sql 组件程序启动后崩溃未初始化数据库连接就执行查询检查 connectDatabase 返回值在查询前确保 db.isOpen() 为 true在这些问题里最需要新手特别注意的是 “Driver not loaded”。Qt 并不直接与 MySQL 通信而是通过驱动插件和 MySQL 客户端库完成协议转换。如果你用的是 Qt 官方预编译包它可能不包含 MySQL 的客户端依赖。解决办法一般是下载 MySQL 的 C 客户端库文件放进 Qt 的驱动目录或者使用包管理器安装libqt6sql6-mysql一类的驱动包。具体文件名和路径依系统和 Qt 版本而异实际操作时先确认你用的是哪个 Qt 版本、哪个编译套件。排查“连接不上”问题有一个通用顺序先命令行确认 MySQL 服务通不通再用图形工具确认账号密码能不能登录最后回到 Qt 代码里看lastError().text()的具体报错。从外到内逐层定位比直接猜代码问题更快。9. 最佳实践与扩展方向9.1 最佳实践数据库密码不要硬编码在源码里。可以先写一个配置文件用QSettings读取避免每次迁移环境都要改代码重新编译。所有 SQL 语句尽量使用preparebindValue不要用字符串拼接。每个槽函数只做一件事。添加、修改、删除各自独立完成后统一调用刷新函数。增删改操作一定要检查exec()的返回值。很多初学者只写执行不写判断出错时完全看不到原因。输出日志用qDebug()配合中文字段能直观看到 SQL 错误信息。发布版本再考虑关闭调试日志。程序退出时调用db.close()避免连接未释放。在真实使用场景中如果涉及用户真实个人信息数据库连接要限制为本机访问密码要使用强密码公开演示前要确认数据已脱敏。9.2 扩展方向跑通基础增删改查之后这个项目可以继续往几个方向扩展难度递进增加登录窗口验证用户名和密码后进入主界面给表格加排序功能点击表头按字段排序用QTableViewQSqlTableModel替代QTableWidget手写刷新逻辑增加分页查询避免数据量大时一次加载过多增加多表关联例如用户表和订单表练习JOIN查询配合QCryptographicHash对密码做 SHA256 散列存储把项目从 Qt Widgets 迁移到 Qt Quick / QML体验另一种界面开发方式增加同步到远程数据库的接口注意必须做好权限控制和数据加密。10. 总结这个项目的核心价值不是“学会了 Qt”或“学会了 MySQL”而是打通了一条完整的工具链你会知道一个桌面软件的界面如何与数据库交互会理解驱动、连接、SQL、结果集这些抽象概念在真实代码里是怎么落地的。以后无论转向 C 后端开发、客户端开发还是继续做更复杂的大型系统这些基础都通用。建议你按这个顺序做先装好 Qt 和 MySQL手工建一张表写一个能连上数据库的 Qt 小程序再逐步把添加、删除、修改、查询四个功能补齐。最先验证的不是界面好不好看而是QSqlDatabase能不能成功open()。这一步走通整个项目就完成了 70%。后面即使遇到乱码、驱动缺失、按钮无响应也都是能按日志逐层排查的问题。这个项目适合作为你的第一个 Qt 实战项目完成做完后把代码整理好保存一套最小可运行配置之后做登录、报表、分页等扩展时都会轻松很多。
返回列表