系统性全面的讲解qt全栈,以及列出详细的步骤和分析和包含内容和要点和案例

系统性全面的讲解qt全栈,以及列出详细的步骤和分析和包含内容和要点和案例

下午4:01

main-thumb-pb-3045-200-wkomexlqbcxpravoniszumuijaodcygr.jpeg

GPT-5

下面是一套系统性、全面的“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、硬件、嵌入式/移动

——

四、详细步骤与要点清单

  1. 项目初始化
  • 选 CMake;设定标准 set(CMAKE_CXX_STANDARD 20)
  • 分层目录:app/ ui/ core/ net/ data/ plugins/ tests/ cmake/
  • 启用自动 MOC/UIC/RCC;设置 Qt 版本与组件
  • 引入 clang-tidy、warnings-as-errors(关键模块)
  1. 选 UI 技术
  • 桌面传统:Widgets 主体 + Model/View
  • 新项目跨平台:QML 主导 + C++ 后端(QQmlApplicationEngine + 注册类型)
  • 统一主题/样式:QSS(Widgets)或 Material/Universal(QML Controls 2)
  1. 模型与数据绑定
  • Widgets:QAbstractItemModel 自定义,配合代理(QSortFilterProxyModel)
  • QML:QAbstractListModel 暴露到 QML,或使用 Q_PROPERTY + NOTIFY 绑定
  • 注意:避免频繁发 NOTIFY,使用 beginResetModel/endResetModel 或批量更新
  1. 网络与存储
  • 封装 HttpClient:超时、重试、重定向、统一错误码、JSON 解析
  • Token 管理与刷新;证书固定(Pinning)与 HSTS(如在后端配合)
  • 数据缓存:SQLite + LRU 缓存(QCache 或自实现)
  1. 并发与任务队列
  • IO 与计算任务放 QThreadPool;UI 更新经信号回主线程
  • 长任务可用协程桥接(co_await Qt Future/自封装)
  1. 日志与配置
  • QLoggingCategory 分类;可切换等级、落盘轮转
  • QSettings 分层环境:dev/staging/prod;敏感信息加密存储
  1. 插件化与脚本
  • 插件接口稳定性(只暴露纯虚接口 + 版本号)
  • QJSEngine 执行业务脚本;或内置 PySide 扩展脚本(需沙箱/权限)
  1. 国际化与可访问性
  • tr() 全覆盖;Linguist 工作流;Qt Quick 的 Accessible 接口
  • RTL 布局、字体回退与 DPI 缩放
  1. 部署与更新
  • 平台化打包:windeployqt 等;创建最小可运行包
  • 在线更新:维护更新通道与差分(zsync、QtIFW 可脚本化)
  1. 测试与 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

main-thumb-pb-3045-200-wkomexlqbcxpravoniszumuijaodcygr.jpeg

GPT-5

很好!下面我针对三个典型目标场景分别提供:技术选型、模块划分、CMake 模板、项目目录、关键实现要点与可运行的最小骨架示例代码。三个场景分别是:

  • 桌面业务系统(Widgets 主导 + 本地 DB + REST 同步)
  • 移动端仪表盘(QML/Qt Quick 主导 + 硬件传感 + 实时渲染/动画)
  • 3D 游戏引擎雏形(Qt Quick 3D/Qt 3D + ECS 架构 + 跨平台)

如果你只需要某一个场景,请告诉我,我可以把该场景扩展成更完整的可运行模板仓库结构与更多示例。

————————
一、桌面业务系统(Widgets + SQLite + HTTP 同步)

  1. 技术选型
  • UI:Qt Widgets + Model/View(QTableView + QAbstractTableModel)
  • 数据:SQLite(Qt Sql),QSettings 配置;JSON 序列化
  • 网络:QNetworkAccessManager + 拦截器(超时/重试/鉴权)
  • 并发:QThreadPool + QtConcurrent
  • 国际化:Qt Linguist
  • 日志:QLoggingCategory + 文件轮转
  • 打包:windeployqt/macdeployqt/linuxdeployqt
  • 测试:Qt Test(单元 + 简单 GUI)
  1. 模块划分
  • app/ 应用入口、主窗体、菜单/动作
  • ui/ 视图与对话框(客户列表、编辑弹窗、设置面板)
  • data/ 数据访问层(Repository、SQLite 迁移)
  • net/ HTTP 客户端、API 封装、鉴权器
  • core/ 领域模型、服务、配置、日志
  • tests/ 单元测试
  • cmake/ 编译器选项、工具宏
  1. 目录与 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)

  1. 可运行的最小示例代码
    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 + 实时数据 + 触控优化)

  1. 技术选型
  • 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 资源
  1. 模块划分
  • app/ 入口、翻译、DPI、高帧率设置
  • ui/ QML 页面、组件、主题、图表/仪表
  • core/ ViewModel、设置、日志
  • io/ WebSocket 或 SerialPort 客户端
  • tests/ 单元与 QML 测试
  1. CMake 最小骨架
    find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick WebSockets)
  • 顶层与子模块类似上一节,不再重复。
  • app 链接 Qt6::Quick Qt6::Qml Qt6::WebSockets
  1. 可运行最小示例
    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 = r
0.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 = r
0.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(-r
0.05, 0); ctx.lineTo(r0.9, 0);
ctx.strokeStyle = "#eee"; ctx.lineWidth = r
0.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(实体/组件/系统),后续可扩展物理、脚本等。
  1. 技术选型
  • 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 格式
  1. 模块划分
  • app/ 引导、窗口、帧循环
  • engine/ ECS 核心(Entity, Transform, System),资源管理
  • scene/ QML 场景与 HUD
  • input/ 输入系统
  • tests/ 单元测试(组件/系统)
  1. CMake 最小骨架
    find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick Quick3D)
  • app 链接 Qt6::Quick Qt6::Quick3D
  • engine 为 STATIC 库
  1. 可运行最小示例
    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 集成
  • 根据你使用的数据库/协议/硬件进行定制实现与演示数据模拟器
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值