系统性全面的讲解qt全栈,以及列出详细的步骤和分析和包含内容和要点和案例
下午4:01
下面是一套系统性、全面的“Qt 全栈”学习与实战指南,覆盖从基础到进阶、从桌面到移动、从前端 UI 到后端服务、从部署到性能与测试的完整路径。内容包含:学习路线图、知识结构、详细步骤、关键要点、常见坑、配套案例与实践建议。
——
一、Qt 全栈的内涵与定位
- 全栈在 Qt 语境下通常指:使用 Qt 实现端侧应用(桌面/移动/嵌入式)的前端界面与业务逻辑,同时对接或内嵌后端能力(本地数据库、网络服务、插件/脚本扩展、跨进程通信),并完成构建、部署、测试与运维。
- Qt 的核心能力:
- 跨平台 UI:Widgets 与 QML/Qt Quick
- C++ 核心库:容器、并发、I/O、网络、多媒体、图形
- 工具链:qmake/CMake、Qt Creator、Designer、Linguist、windeployqt/macdeployqt/linuxdeployqt
- 模块生态:Qt Network、Sql、WebSockets、WebEngine、Multimedia、Charts、3D、Bluetooth、SerialPort、Sensors
- 嵌入式与移动:Qt for MCU/Embedded、Android/iOS 打包
- 可扩展:插件系统、Python 绑定(PySide6)、脚本集成(QJSEngine/QML JS)
——
二、学习与实施路线图(阶段化)
阶段 0:环境与基础(1–2 周)
- 安装与选择版本:Qt 6.x(建议 LTS,如 6.5/6.8),Qt Creator,CMake(推荐)或 qmake
- 语言与标准:现代 C++17/20,掌握 RAII、智能指针、lambda、constexpr、ranges(可选)
- Qt 核心机制:
- 元对象系统、moc、信号与槽(新语法:connect(obj, &Type::signal, this, &Type::slot))
- QObject 生命周期、父子对象树
- 事件循环与线程亲和性
- 资源系统(.qrc)
- 快速练习:控制台 + 简单 Widgets 窗口 + 简单 QML 界面
阶段 1:UI 技术栈选型与掌握(2–4 周)
- Widgets vs QML:
- Widgets:成熟稳定、原生外观、桌面优先、传统 MVC
- QML/Qt Quick:声明式、动画流畅、跨平台一致、移动与嵌入式友好
- 路线建议:
- 桌面办公/工业控制/传统系统:Widgets 主体 + 可嵌 QML 组件
- 面向消费级/移动/仪表盘/大屏:QML 优先 + C++ 后端
- 必修内容:
- Widgets:布局管理、Model/View(QTableView + QAbstractTableModel)、对话框、动作/菜单/工具栏、样式表
- QML:Item/Rectangle/Text、布局(Row/Column/Grid/Anchors)、状态/过渡、ListView/Repeater/Delegate、Bindings、JS 逻辑、QtQuick.Controls 2、主题与样式、自适配 DPI
- 图形与性能:Qt Quick Scene Graph、ShaderEffect(基础即可)
阶段 2:数据与业务层(2–3 周)
- 数据访问:
- Qt Sql:QSqlDatabase/QSqlQuery/QSqlTableModel;驱动(SQLite/MySQL/PostgreSQL)
- 序列化:QJsonDocument/QJsonObject/QCbor、QDataStream、Protobuf(第三方)
- 配置管理:QSettings + 加密(如 QCryptographicHash/HMAC,自定义加解密)
- 业务组织:
- Model-View-ViewModel(MVVM for QML),或 Presenter(MVP for Widgets)
- Domain 层与 DTO/ValueObject,错误处理(QException 自定义或 std::expected 风格)
- 状态机:QStateMachine 或 SCXML
阶段 3:网络与后端(3–4 周)
- 客户端网络:
- QNetworkAccessManager(HTTP/HTTPS)、拦截器/重试、超时、并发
- WebSocket(QWebSocket)、gRPC/REST(第三方封装)
- 认证:OAuth2(Qt NetworkAuth)、JWT、证书校验(QSslConfiguration)
- 本地后端与进程间通信:
- 本地服务:QtHttpServer(6.7+ 可选模块)或 QTcpServer/QUdpSocket 自建
- IPC:QLocalSocket/QLocalServer、QSharedMemory、QProcess、DBus(Linux)
- 插件架构:QPluginLoader + 接口类(Q_DECLARE_INTERFACE)
- Web 嵌入:
- Qt WebEngine(Chromium 内核)嵌网页/Hybrid
- 与 JS 互操作:QWebChannel
阶段 4:并发、任务与响应性(1–2 周)
- 线程模型:
- 主线程 UI 原则;工作线程:QThread、QRunnable + QThreadPool
- 异步 API:信号/槽、Future(QtConcurrent::run)、协程(C++20 与 Qt 结合策略)
- 性能:对象数量、绑定评估次数、减少重复布局/绘制、资源懒加载
阶段 5:工程化、测试与质量(2–3 周)
- 构建与打包:
- CMake 最佳实践:项目分层、INTERFACE 库、目标属性、跨平台工具链
- 资源/翻译(lupdate/lrelease/Qt Linguist)、国际化 I18N/L10N
- 部署:windeployqt/macdeployqt/linuxdeployqt;Android/iOS 打包签名;嵌入式 Yocto/Buildroot
- 测试:
- Qt Test(QTest):单元测试、GUI 测试(QSignalSpy、QTest::mouseClick)
- 集成测试:Headless/CI(GitHub Actions/GitLab CI)、Artifact 打包
- 代码质量:
- 静态分析:Clang-Tidy、Cppcheck、Sanitizers
- 日志与监控:QLoggingCategory、崩溃收集(Breakpad/Sentry)
阶段 6:高级主题与行业化(按需)
- 图表与数据可视化:Qt Charts、QCustomPlot(第三方)
- 多媒体:Qt Multimedia,摄像头、音频采集与回放、硬编解码
- 3D 与可视化:Qt 3D、Qt Quick 3D、OpenGL/Vulkan 集成
- 硬件与通讯:SerialPort、Bluetooth、CAN Bus、Sensors
- 安全:证书校验、Code Signing、数据加密、反调试/混淆(合规前提)
- 脚本与扩展:QJSEngine/QML JS、Python 绑定(PySide6)
——
三、知识结构图(文字版)
- 语言与工具:C++17/20、CMake、Qt Creator、调试器
- Qt 基础:QObject/信号槽/事件循环/资源系统/内存管理
- UI 层:Widgets 或 QML(控件、布局、主题、动画、可访问性)
- 数据层:配置、序列化、数据库、缓存
- 业务层:模型与状态、服务接口、错误与日志
- 网络层:HTTP/WebSocket/Socket、认证与安全、重连策略
- 并发与性能:线程、任务、锁/无锁、渲染性能
- 工程化:模块化、测试、CI/CD、部署、国际化、版本与配置管理
- 扩展与平台:WebEngine、多媒体、3D、硬件、嵌入式/移动
——
四、详细步骤与要点清单
- 项目初始化
- 选 CMake;设定标准 set(CMAKE_CXX_STANDARD 20)
- 分层目录:app/ ui/ core/ net/ data/ plugins/ tests/ cmake/
- 启用自动 MOC/UIC/RCC;设置 Qt 版本与组件
- 引入 clang-tidy、warnings-as-errors(关键模块)
- 选 UI 技术
- 桌面传统:Widgets 主体 + Model/View
- 新项目跨平台:QML 主导 + C++ 后端(QQmlApplicationEngine + 注册类型)
- 统一主题/样式:QSS(Widgets)或 Material/Universal(QML Controls 2)
- 模型与数据绑定
- Widgets:QAbstractItemModel 自定义,配合代理(QSortFilterProxyModel)
- QML:QAbstractListModel 暴露到 QML,或使用 Q_PROPERTY + NOTIFY 绑定
- 注意:避免频繁发 NOTIFY,使用 beginResetModel/endResetModel 或批量更新
- 网络与存储
- 封装 HttpClient:超时、重试、重定向、统一错误码、JSON 解析
- Token 管理与刷新;证书固定(Pinning)与 HSTS(如在后端配合)
- 数据缓存:SQLite + LRU 缓存(QCache 或自实现)
- 并发与任务队列
- IO 与计算任务放 QThreadPool;UI 更新经信号回主线程
- 长任务可用协程桥接(co_await Qt Future/自封装)
- 日志与配置
- QLoggingCategory 分类;可切换等级、落盘轮转
- QSettings 分层环境:dev/staging/prod;敏感信息加密存储
- 插件化与脚本
- 插件接口稳定性(只暴露纯虚接口 + 版本号)
- QJSEngine 执行业务脚本;或内置 PySide 扩展脚本(需沙箱/权限)
- 国际化与可访问性
- tr() 全覆盖;Linguist 工作流;Qt Quick 的 Accessible 接口
- RTL 布局、字体回退与 DPI 缩放
- 部署与更新
- 平台化打包:windeployqt 等;创建最小可运行包
- 在线更新:维护更新通道与差分(zsync、QtIFW 可脚本化)
- 测试与 CI
- Qt Test 编写用例;截图比对(可阈值)
- GitHub Actions:多平台构建矩阵、缓存构建工件
——
五、典型案例与示例结构
案例 A:桌面数据管理工具(Widgets + SQLite + HTTP 同步)
- 功能点:
- 数据表格浏览/编辑(QTableView + 自定义 QAbstractTableModel)
- 本地 SQLite 缓存,后台同步远端 REST
- 登录/OAuth2、配置界面、日志面板
- 技术要点:
- 统一 HttpClient(QNetworkAccessManager + 拦截器)
- 事务性写入(QSqlDatabase::transaction)
- Model 变更批处理,避免 UI 卡顿
- 关键代码片段(思路级):
- 自定义模型:rowCount/data/setData/flags;提交时批量 SQL
- 同步器:定时器 + ETag/If-Modified-Since 差量拉取
案例 B:跨平台现代 UI(QML + C++ 后端 + WebSocket)
- 功能点:
- 动态列表与图表、动画过渡、深色模式
- 实时数据订阅(WebSocket),离线缓存
- 技术要点:
- C++ 暴露模型到 QML(qmlRegisterType 或 setContextProperty)
- QML 性能:减少绑定链、使用 Loader 延迟加载、图片缓存
- WebSocket 自动重连与指数回退
案例 C:Hybrid 嵌网页(Qt WebEngine + QWebChannel)
- 功能点:
- 嵌入企业已有 Web 前端,与本地硬件交互
- 技术要点:
- WebChannel 暴露 C++ 对象到 JS;双向调用
- 权限与安全沙箱,限制导航与下载策略
案例 D:嵌入式仪表盘(Qt Quick + SerialPort/Sensors)
- 功能点:
- 高刷新仪表组件、触控优化、硬件传感数据
- 技术要点:
- 固定刷新节流、合批绘制、ShaderEffect 做指针阴影
- 零拷贝数据路径、线程亲和与 RT 限制
——
六、常见难点与避坑
- 信号与槽泄漏/重复连接:使用 Qt::UniqueConnection;或在构造一次连接、析构自动断开
- 线程与对象亲和:QObject 只能在其所属线程使用;跨线程用 queued connection
- QML 循环绑定/频繁重算:分离只读属性、使用 Binding.when、避免在 onXXX 中 set 自己
- 网络超时与 SSL:默认无总超时,需自设 QTimer;证书校验严格处理错误回调
- 数据库驱动:部署需带上对应插件与依赖(如 libmysqlclient)
- DPI 自适配:启用 High DPI 属性;图片用 SVG 或多分辨率
- WebEngine 体积:注意打包大小与沙箱开关;CEF/原生页权衡
- 构建缓存与模块化:拆分子库,避免全量重编译;启用 ccache/sccache
——
七、参考的项目骨架(CMake + Qt 6 + QML 示意)
- 目标:QML 前端 + C++ 后端 + 网络 + SQLite + 测试 + 打包
- 要点:模块化、可测试、可扩展
CMake 内容要点
- 设定 Qt 组件:Core, Gui, Qml, Quick, Network, Sql, WebSockets, Test
- 每个子目录一个目标,使用 target_link_libraries 精准链接
- 开启 AUTOMOC/AUTOUIC/AUTORCC
目录结构
- app/ 主程序入口(main.cpp, qml.qrc)
- ui/ QML 组件与资源(Controls、Pages、Themes)
- core/ 领域模型与服务(Settings、Logger、Auth)
- net/ HttpClient、WebSocketClient
- data/ Repository、Migrations、Sqlite helper
- plugins/ 插件接口与实现
- tests/ 单元/集成测试
- cmake/ 工具模块(Lint、Compiler options)
代码设计要点
- main.cpp:启用高 DPI、加载翻译、注册类型、加载主 QML
- 后端通过单例/上下文注入到 QML;只把必要接口暴露到 UI 层
- 严格区分线程边界:网络与 DB 可独立线程池
——
八、性能优化清单
- 启动:懒加载 QML、拆分资源包、缩短冷启动路径
- 渲染:减少层级、合批、避免频繁阴影与模糊、必要时用 ShaderEffectCache
- 数据:批量更新模型、分页加载、虚拟化列表
- 并发:线程池大小 = 核心数;避免过度锁;使用无锁结构(环形缓冲)
- I/O:异步网络、流式解析 JSON(SAX 样式或局部 dom)
- 工具:QML Profiler、GammaRay、perf/VTune、Qt Creator Analyzer
——
九、部署与运维
- 桌面:
- Windows:windeployqt + NSIS/IFW 安装包;签名;VC 运行库
- macOS:macdeployqt + notarization;沙箱权限(App Sandbox 可选)
- Linux:linuxdeployqt/AppImage/Flatpak;依赖策略
- 移动:
- Android:Gradle 签名、ABI 拓扑(armeabi-v7a/arm64-v8a/x86_64)
- iOS:Provisioning Profile、Bitcode/新架构设置、ATS
- 更新:
- 应用内更新通道;校验签名;回滚机制
- 监控:
- 崩溃收集与符号化;日志远程上报(脱敏)
——
十、实践计划与里程碑
- 第 1–2 周:基础语法 + Qt 核心机制 + 两个 Hello 项目(Widgets/QML 各一个)
- 第 3–5 周:确定 UI 栈;完成一个 CRUD + REST 同步的小工具
- 第 6–8 周:引入 WebSocket 实时、缓存、国际化、测试与 CI
- 第 9–12 周:优化性能、插件化、打包与自动更新;撰写部署脚本
- 最终交付:一套跨平台应用,含文档、测试覆盖、CI/CD、安装包
——
十一、延伸与资源
- 官方文档与示例:doc.qt.io(模块按需深读)
- 源码与社区:Qt 源码、KDAB/KDAB Talks、Qt Blog、ICS 博客
- 工具:GammaRay(KDAB)、qmlformat/qmllint、Clang-Tidy 配置
- 书籍建议:
- C++ 并发实战、Effective Modern C++
- QML Book(在线)、Advanced Qt Programming(适合 Widgets/Model-View)
——
如果你愿意,我可以基于你的目标场景(例如“桌面业务系统”或“移动端仪表盘”)定制一套更具体的技术选型、模块划分、CMake 模板和示例代码。也可以给出一个完整可运行的入门项目骨架。
下午4:01
目标场景(例如“桌面业务系统”或“移动端仪表盘”,3d游戏引擎)定制一套更具体的技术选型、模块划分、CMake 模板和示例代码。也可以给出一个完整可运行的入门项目骨架。
下午4:03
很好!下面我针对三个典型目标场景分别提供:技术选型、模块划分、CMake 模板、项目目录、关键实现要点与可运行的最小骨架示例代码。三个场景分别是:
- 桌面业务系统(Widgets 主导 + 本地 DB + REST 同步)
- 移动端仪表盘(QML/Qt Quick 主导 + 硬件传感 + 实时渲染/动画)
- 3D 游戏引擎雏形(Qt Quick 3D/Qt 3D + ECS 架构 + 跨平台)
如果你只需要某一个场景,请告诉我,我可以把该场景扩展成更完整的可运行模板仓库结构与更多示例。
————————
一、桌面业务系统(Widgets + SQLite + HTTP 同步)
- 技术选型
- UI:Qt Widgets + Model/View(QTableView + QAbstractTableModel)
- 数据:SQLite(Qt Sql),QSettings 配置;JSON 序列化
- 网络:QNetworkAccessManager + 拦截器(超时/重试/鉴权)
- 并发:QThreadPool + QtConcurrent
- 国际化:Qt Linguist
- 日志:QLoggingCategory + 文件轮转
- 打包:windeployqt/macdeployqt/linuxdeployqt
- 测试:Qt Test(单元 + 简单 GUI)
- 模块划分
- app/ 应用入口、主窗体、菜单/动作
- ui/ 视图与对话框(客户列表、编辑弹窗、设置面板)
- data/ 数据访问层(Repository、SQLite 迁移)
- net/ HTTP 客户端、API 封装、鉴权器
- core/ 领域模型、服务、配置、日志
- tests/ 单元测试
- cmake/ 编译器选项、工具宏
- 目录与 CMake 最小骨架
CMakeLists.txt(顶层)
- 设置 C++20,启用 AUTOMOC/AUTOUIC/AUTORCC
- 找到 Qt6::Core Gui Widgets Network Sql Test
示例顶层 CMakeLists.txt
- 说明:直接可用;按需调整 Qt 版本和可选模块。
cmake_minimum_required(VERSION 3.21)
project(BizDesk LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
编译器警告与优化
if(MSVC)
add_compile_options(/W4 /permissive-)
else()
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
Qt
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Gui Widgets Network Sql Test)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
add_subdirectory(core)
add_subdirectory(net)
add_subdirectory(data)
add_subdirectory(ui)
add_subdirectory(app)
add_subdirectory(tests)
core/CMakeLists.txt
add_library(core STATIC
include/core/domain.hpp
include/core/logger.hpp
src/logger.cpp
src/domain.cpp
)
target_include_directories(core PUBLIC include)
target_link_libraries(core PUBLIC Qt6::Core)
net/CMakeLists.txt
add_library(net STATIC
include/net/http_client.hpp
include/net/auth.hpp
src/http_client.cpp
src/auth.cpp
)
target_include_directories(net PUBLIC include)
target_link_libraries(net PUBLIC Qt6::Core Qt6::Network core)
data/CMakeLists.txt
add_library(data STATIC
include/data/repository.hpp
include/data/migrations.hpp
src/repository.cpp
src/migrations.cpp
)
target_include_directories(data PUBLIC include)
target_link_libraries(data PUBLIC Qt6::Core Qt6::Sql core)
ui/CMakeLists.txt
add_library(ui STATIC
include/ui/main_window.hpp
include/ui/customer_model.hpp
src/main_window.cpp
src/customer_model.cpp
)
target_include_directories(ui PUBLIC include)
target_link_libraries(ui PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets core data net)
app/CMakeLists.txt
add_executable(BizDesk
src/main.cpp
resources/resources.qrc
)
target_link_libraries(BizDesk PRIVATE ui)
tests/CMakeLists.txt
enable_testing()
add_executable(tests_core tests/test_domain.cpp)
target_link_libraries(tests_core PRIVATE Qt6::Test core)
add_test(NAME tests_core COMMAND tests_core)
- 可运行的最小示例代码
core/include/core/domain.hpp
#pragma once
#include <QString>
#include <QDateTime>
struct Customer {
int id = -1;
QString name;
QString email;
QDateTime updatedAt;
};
core/include/core/logger.hpp
#pragma once
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(catApp)
core/src/logger.cpp
#include "core/logger.hpp"
Q_LOGGING_CATEGORY(catApp, "app")
net/include/net/http_client.hpp
#pragma once
#include <QObject>
#include <QNetworkAccessManager>
#include <QJsonObject>
#include <functional>
class HttpClient : public QObject {
Q_OBJECT
public:
explicit HttpClient(QObject* parent=nullptr);
void setBaseUrl(const QUrl& u);
void setToken(const QString& t);
void get(const QString& path, std::function<void(QByteArray, QString)> cb);
private:
QNetworkAccessManager nam_;
QUrl base_;
QString token_;
};
net/src/http_client.cpp
#include "net/http_client.hpp"
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTimer>
HttpClient::HttpClient(QObject* p):QObject(p){}
void HttpClient::setBaseUrl(const QUrl& u){ base_=u; }
void HttpClient::setToken(const QString& t){ token_=t; }
void HttpClient::get(const QString& path, std::function<void(QByteArray, QString)> cb) {
QNetworkRequest req(base_.resolved(QUrl(path)));
if(!token_.isEmpty()) req.setRawHeader("Authorization", ("Bearer "+token_).toUtf8());
auto* reply = nam_.get(req);
// 总超时
auto timer = new QTimer(reply);
timer->setSingleShot(true);
timer->start(10000);
connect(timer,&QTimer::timeout, reply, [reply,cb]{ reply->abort(); cb({}, "timeout"); });
connect(reply,&QNetworkReply::finished, reply, [reply,cb]{
reply->deleteLater();
if(reply->error()!=QNetworkReply::NoError){ cb({}, reply->errorString()); return; }
cb(reply->readAll(), {});
});
}
data/include/data/repository.hpp
#pragma once
#include <QObject>
#include <QSqlDatabase>
#include "core/domain.hpp"
class Repository : public QObject {
Q_OBJECT
public:
explicit Repository(QObject* parent=nullptr);
bool open(const QString& path, QString* err=nullptr);
QList<Customer> listCustomers();
bool upsert(const Customer& c);
private:
QSqlDatabase db_;
};
data/include/data/migrations.hpp
#pragma once
#include <QSqlDatabase>
bool runMigrations(QSqlDatabase& db, QString* err);
data/src/migrations.cpp
#include "data/migrations.hpp"
#include <QSqlQuery>
#include <QSqlError>
bool runMigrations(QSqlDatabase& db, QString* err){
QSqlQuery q(db);
if(!q.exec("CREATE TABLE IF NOT EXISTS customer ("
"id INTEGER PRIMARY KEY, name TEXT, email TEXT, updated_at TEXT)")){
if(err) *err = q.lastError().text();
return false;
}
return true;
}
data/src/repository.cpp
#include "data/repository.hpp"
#include "data/migrations.hpp"
#include <QSqlQuery>
#include <QSqlError>
Repository::Repository(QObject* p):QObject(p){}
bool Repository::open(const QString& path, QString* err){
db_ = QSqlDatabase::addDatabase("QSQLITE");
db_.setDatabaseName(path);
if(!db_.open()){ if(err) *err=db_.lastError().text(); return false; }
return runMigrations(db_, err);
}
QList<Customer> Repository::listCustomers(){
QList<Customer> out;
QSqlQuery q(db_);
q.exec("SELECT id,name,email,updated_at FROM customer ORDER BY id DESC");
while(q.next()){
Customer c;
c.id = q.value(0).toInt();
c.name = q.value(1).toString();
c.email = q.value(2).toString();
c.updatedAt = QDateTime::fromString(q.value(3).toString(), Qt::ISODate);
out.push_back(c);
}
return out;
}
bool Repository::upsert(const Customer& c){
QSqlQuery q(db_);
q.prepare("INSERT INTO customer(id,name,email,updated_at) VALUES(?,?,?,?) "
"ON CONFLICT(id) DO UPDATE SET name=excluded.name,email=excluded.email,updated_at=excluded.updated_at");
q.addBindValue(c.id);
q.addBindValue(c.name);
q.addBindValue(c.email);
q.addBindValue(c.updatedAt.toString(Qt::ISODate));
return q.exec();
}
ui/include/ui/customer_model.hpp
#pragma once
#include <QAbstractTableModel>
#include "core/domain.hpp"
class CustomerModel : public QAbstractTableModel {
Q_OBJECT
public:
explicit CustomerModel(QObject* parent=nullptr);
int rowCount(const QModelIndex&) const override;
int columnCount(const QModelIndex&) const override { return 3; }
QVariant data(const QModelIndex&, int) const override;
QVariant headerData(int, Qt::Orientation, int) const override;
void setItems(QList<Customer> items);
private:
QList<Customer> items_;
};
ui/src/customer_model.cpp
#include "ui/customer_model.hpp"
CustomerModel::CustomerModel(QObject* p):QAbstractTableModel(p){}
int CustomerModel::rowCount(const QModelIndex&) const { return items_.size(); }
QVariant CustomerModel::data(const QModelIndex& idx, int role) const {
if(!idx.isValid() || role!=Qt::DisplayRole) return {};
const auto& c = items_.at(idx.row());
switch(idx.column()){
case 0: return c.name;
case 1: return c.email;
case 2: return c.updatedAt.toString(Qt::ISODate);
}
return {};
}
QVariant CustomerModel::headerData(int s, Qt::Orientation o, int role) const {
if(o==Qt::Horizontal && role==Qt::DisplayRole){
switch(s){ case 0: return "Name"; case 1: return "Email"; case 2: return "Updated"; }
}
return {};
}
void CustomerModel::setItems(QList<Customer> items){
beginResetModel();
items_ = std::move(items);
endResetModel();
}
ui/include/ui/main_window.hpp
#pragma once
#include <QMainWindow>
class QTableView;
class CustomerModel;
class Repository;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(Repository* repo, QWidget* parent=nullptr);
private slots:
void reload();
private:
Repository* repo_;
QTableView* table_;
CustomerModel* model_;
};
ui/src/main_window.cpp
#include "ui/main_window.hpp"
#include "ui/customer_model.hpp"
#include "data/repository.hpp"
#include <QTableView>
#include <QVBoxLayout>
#include <QToolBar>
#include <QAction>
MainWindow::MainWindow(Repository* repo, QWidget* parent)
: QMainWindow(parent), repo_(repo) {
auto* tb = addToolBar("Main");
auto* actReload = tb->addAction("Reload");
connect(actReload, &QAction::triggered, this, &MainWindow::reload);
reasonml
table_ = new QTableView(this);
model_ = new CustomerModel(this);
table_->setModel(model_);
setCentralWidget(table_);
reload();
}
void MainWindow::reload(){
model_->setItems(repo_->listCustomers());
}
app/src/main.cpp
#include <QApplication>
#include "data/repository.hpp"
#include "ui/main_window.hpp"
int main(int argc, char** argv){
QApplication app(argc, argv);
Repository repo;
QString err;
if(!repo.open("bizdesk.db", &err)){
qWarning() << "DB open failed:" << err;
}
MainWindow w(&repo);
w.resize(900,600);
w.show();
return app.exec();
}
app/resources/resources.qrc
<RCC><qresource prefix="/"><file>icons/app.svg</file></qresource></RCC>
tests/tests/test_domain.cpp
#include <QtTest>
#include "core/domain.hpp"
class DomainTest: public QObject{ Q_OBJECT
private slots:
void basic(){ Customer c; c.name="A"; QVERIFY(c.name=="A"); }
};
QTEST_MAIN(DomainTest)
#include "test_domain.moc"
运行
- mkdir build && cd build && cmake -S .. -B . && cmake --build . -j
- 运行 BizDesk(首次会创建 SQLite 表)
————————
二、移动端仪表盘(Qt Quick + 实时数据 + 触控优化)
- 技术选型
- UI:QML/Qt Quick + QtQuick.Controls 2(Material/Universal)
- 实时:Qt WebSocket 或 SerialPort/Sensors;Scene Graph 自定义绘制或 ShaderEffect
- 数据模型:QAbstractListModel 暴露给 QML;MVVM
- 性能:Loader 延迟加载、Binding 优化、统一 60 FPS 预算
- 部署:Android/iOS 打包;多 ABI;高 DPI
- 构建:CMake + qml.qrc 资源
- 模块划分
- app/ 入口、翻译、DPI、高帧率设置
- ui/ QML 页面、组件、主题、图表/仪表
- core/ ViewModel、设置、日志
- io/ WebSocket 或 SerialPort 客户端
- tests/ 单元与 QML 测试
- CMake 最小骨架
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick WebSockets)
- 顶层与子模块类似上一节,不再重复。
- app 链接 Qt6::Quick Qt6::Qml Qt6::WebSockets
- 可运行最小示例
app/src/main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "io/realtime_client.hpp"
int main(int argc, char* argv[]){
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
RealTimeClient client;
engine.rootContext()->setContextProperty("rtClient", &client);
engine.load(QUrl(QStringLiteral("qrc:/ui/main.qml")));
if(engine.rootObjects().isEmpty()) return -1;
return app.exec();
}
io/realtime_client.hpp
#pragma once
#include <QObject>
#include <QTimer>
#include <QDateTime>
class RealTimeClient : public QObject {
Q_OBJECT
Q_PROPERTY(double value READ value NOTIFY valueChanged)
public:
RealTimeClient(QObject* p=nullptr):QObject(p){
connect(&timer_, &QTimer::timeout, this, [this]{
val_ = 50.0 + 50.0 * std::sin(QDateTime::currentMSecsSinceEpoch()/500.0);
emit valueChanged();
});
timer_.start(16);
}
double value() const { return val_; }
signals:
void valueChanged();
private:
QTimer timer_;
double val_ = 0.0;
};
ui/qml.qrc
<RCC><qresource prefix="/ui"><file>main.qml</file><file>Gauge.qml</file></qresource></RCC>
ui/main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
width: 420; height: 780; visible: true; title: "仪表盘"
color: "#111"
Column {
anchors.centerIn: parent; spacing: 24
Gauge {
width: Math.min(parent.width*0.8, 320); height: width
value: rtClient.value
}
Slider {
from: 0; to: 100; value: 50
onValueChanged: rtClient.objectName = "" // 占位,示例不修改 client
}
}
}
ui/Gauge.qml
import QtQuick 2.15
Item {
property real value: 0
Canvas {
id: c; anchors.fill: parent; antialiasing: true
onPaint: {
const ctx = getContext("2d");
const w = width, h = height, cx = w/2, cy = h/2, r = Math.min(w,h)/2 * 0.9;
ctx.reset();
ctx.clearRect(0,0,w,h);
// 背景环
ctx.beginPath(); ctx.arc(cx,cy,r,0,Math.PI2);
ctx.strokeStyle = "#333"; ctx.lineWidth = r0.15; ctx.stroke();
// 进度
let ang = (value/100.0) * Math.PI1.5 + Math.PI0.75;
ctx.beginPath(); ctx.arc(cx,cy,r,Math.PI0.75, ang);
ctx.strokeStyle = "#40c463"; ctx.lineWidth = r0.15; ctx.lineCap = "round"; ctx.stroke();
// 指针
ctx.save();
ctx.translate(cx,cy); ctx.rotate((value/100.0)Math.PI1.5 - Math.PI0.75);
ctx.beginPath(); ctx.moveTo(-r0.05, 0); ctx.lineTo(r0.9, 0);
ctx.strokeStyle = "#eee"; ctx.lineWidth = r0.04; ctx.stroke();
ctx.restore();
// 文本
ctx.fillStyle = "#ddd"; ctx.font = ${r*0.22}px sans-serif;
ctx.textAlign = "center"; ctx.fillText(value.toFixed(0), cx, cy + r*0.2);
}
}
onValueChanged: c.requestPaint()
}
运行
- cmake 构建后运行,看到实时摆动的仪表。替换 RealTimeClient 为 WebSocket/SerialPort 即可接入真实数据。
————————
三、3D 游戏引擎雏形(Qt Quick 3D + 简易 ECS)
说明
- 目标是给出一个“能跑起来”的 3D 场景,带输入、摄像机、基本 ECS(实体/组件/系统),后续可扩展物理、脚本等。
- 技术选型
- 3D 渲染:Qt Quick 3D(Qt 6.5+),或 Qt 3D(较旧)
- 场景/UI:QML(QtQuick 3D + Controls 2)
- 架构:简易 ECS(C++),把数据暴露给 QML 控制器
- 资源:.mesh/.glb;运行时材质(PrincipledMaterial)
- 输入:QML 的 KeyHandler、PointerHandler
- 计时:QAnimationDriver 或帧定时器(beforeRendering 信号)
- 可选:Assimp 导入(工具链),或用 Qt Quick 3D 的 Baked 格式
- 模块划分
- app/ 引导、窗口、帧循环
- engine/ ECS 核心(Entity, Transform, System),资源管理
- scene/ QML 场景与 HUD
- input/ 输入系统
- tests/ 单元测试(组件/系统)
- CMake 最小骨架
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Quick3D)
- app 链接 Qt6::Quick Qt6::Quick3D
- engine 为 STATIC 库
- 可运行最小示例
engine/include/ecs.hpp
#pragma once
#include <QVector3D>
#include <QQuaternion>
#include <vector>
#include <cstdint>
using Entity = uint32_t;
struct Transform {
QVector3D position{0,0,0};
QQuaternion rotation{};
QVector3D scale{1,1,1};
};
class Registry {
public:
Entity create(){ return next_++; }
Transform& addTransform(Entity e){ if(e>=transforms_.size()) transforms_.resize(e+1); return transforms_[e]; }
Transform* getTransform(Entity e){ return (e<transforms_.size()) ? &transforms_[e] : nullptr; }
private:
Entity next_ = 0;
std::vector<Transform> transforms_;
};
engine/include/engine.hpp
#pragma once
#include "ecs.hpp"
#include <QObject>
#include <QElapsedTimer>
class Engine : public QObject {
Q_OBJECT
public:
explicit Engine(QObject* p=nullptr);
Q_INVOKABLE void tick(double dt);
Q_INVOKABLE QVector3D cameraPos() const { return camPos_; }
Q_INVOKABLE void moveForward(double d){ camPos_.setZ(camPos_.z()-d); }
Q_INVOKABLE void moveRight(double d){ camPos_.setX(camPos_.x()+d); }
private:
Registry reg_;
Entity cube_{0};
QVector3D camPos_{0, 1.8f, 5.0f};
};
engine/src/engine.cpp
#include "engine.hpp"
Engine::Engine(QObject* p):QObject(p){
cube_ = reg_.create();
auto& t = reg_.addTransform(cube_);
t.position = {0, 0.5f, 0};
}
app/src/main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "engine/engine.hpp"
int main(int argc, char** argv){
QGuiApplication app(argc, argv);
QQmlApplicationEngine eng;
Engine engine;
eng.rootContext()->setContextProperty("Engine", &engine);
eng.load(QUrl("qrc:/scene/Main.qml"));
if(eng.rootObjects().isEmpty()) return -1;
return app.exec();
}
scene/scene.qrc
<RCC><qresource prefix="/scene">
<file>Main.qml</file>
</qresource></RCC>
scene/Main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick3D 1.15
ApplicationWindow {
visible: true; width: 1024; height: 640; color: "#1a1a1a"; title: "Qt 3D Mini Engine"
Shortcut { sequences: ["W"]; onActivated: Engine.moveForward(0.1) }
Shortcut { sequences: ["S"]; onActivated: Engine.moveForward(-0.1) }
Shortcut { sequences: ["A"]; onActivated: Engine.moveRight(-0.1) }
Shortcut { sequences: ["D"]; onActivated: Engine.moveRight(0.1) }
Row {
anchors.fill: parent
View3D {
id: v3d
anchors.fill: parent
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.Color
clearColor: "#202228"
}
PerspectiveCamera {
id: cam
position: Qt.vector3d(Engine.cameraPos().x, Engine.cameraPos().y, Engine.cameraPos().z)
eulerRotation.x: -10
clipFar: 200
}
DirectionalLight { eulerRotation.x: -45; eulerRotation.y: 45; castsShadow: true; brightness: 80 }
Model {
source: "#Cube"; scale: Qt.vector3d(1,1,1)
materials: DefaultMaterial { diffuseColor: "#4fc3f7" }
}
Model {
source: "#Plane"; scale: Qt.vector3d(10,1,10); position.y: -0.01
materials: DefaultMaterial { diffuseColor: "#37474f" }
}
}
}
}
注意
- 使用 Qt Quick 3D 自带的内置几何体("#Cube", "#Plane")保证最小可运行。
- 后续拓展:将 Registry 与 QML 绑定,QML 中以 Repeater 绑定实体渲染;加入帧计时器驱动 Engine.tick(dt)。
————————
通用构建与运行说明
- 要求:Qt 6.5+(LTS 更佳),CMake 3.21+
- 构建步骤:
- mkdir build && cd build
- cmake -S .. -B . -DCMAKE_BUILD_TYPE=Release
- cmake --build . -j
- 运行对应可执行文件
- 部署:
- Windows: windeployqt path/to/exe
- macOS: macdeployqt App.app
- Linux: linuxdeployqt 或打包成 AppImage/Flatpak
————————
如何选择与下一步
- 如果你的重点是企业桌面生产力工具,优先使用“桌面业务系统”模板,先把数据模型/同步/权限打通。
- 如果你的重点是硬件展示与交互,采用“移动端仪表盘”模板,先联上真实数据源,调优渲染与动画。
- 如果你的重点是 3D 内容与引擎研发,采用“3D 引擎雏形”,逐步引入资源加载、输入映射、相机控制、光照与材质系统,并计划物理与脚本。
告诉我你确定采用的场景(或要组合多个场景),我可以:
- 生成对应完整可运行仓库的所有 CMake 与代码文件
- 补充 CI 配置(GitHub Actions 多平台矩阵)
- 加上安装包脚本与 windeployqt/macdeployqt 集成
- 根据你使用的数据库/协议/硬件进行定制实现与演示数据模拟器



被折叠的 条评论
为什么被折叠?



