简介:这是一份面向Qt学习者与开发者的实战教程资源,配合霍亚飞所著《QT和QT quick实战》的项目源码,以完整程序驱动学习,帮助解决Qt理论抽象、难以独立上手的问题。资源包共五百三十五个文件,其中包含七十九个C++程序文件、五十五个头文件、六十七个QML界面描述、二十九个工程文件与二十六个界面文件,还搭配图片与音频素材,容量约五十八点五三MB。整体结构按模块划分,查找起来十分方便。目前已有两千五百一十三人进行学习。通过阅读源码,可掌握信号与槽通信机制、QML动画与状态机、网络与数据库访问、跨平台适配等核心技巧;手动运行调试后,能透彻理解组件封装和界面动态更新的实现思路。书中代码由浅入深,从基础控件到多媒体播放均有覆盖,适合结合文档边学边练,快速形成从功能设计到发布部署的完整经验。
1. QT 实战源码包到底解决什么
很多人学 Qt 是“看视频全会、一动手就废”:跟着教程点一遍控件,离开教程连一个带界面的小工具都拼不出来。这套 QT 和 QT quick 实战源码包解决的就是这个问题——它不是零散的代码片段,而是一整套能直接编译运行的工程,覆盖 QWidget 传统控件、Qt Quick 的 QML 界面,以及 C++ 与 QML 之间的数据桥接。拿到手你会发现它不是单文件,而是多个可独立构建的模拟项目,比如带实时曲线的仪表盘 Demo、某跨平台系统的小工具骨架。适合两类人:刚开始学 Qt、需要一份能边改边看的完整源码的初学者,以及已经把 Qt 装好但始终没跑通“QML + C++ 混合工程”的在职开发者。源码的价值不在代码量,在于每一条链路都能编译、能修改、能验证你对 Qt 的理解。
2. 把环境钉死:构建链、工程骨架与第一个可运行 Demo
源码包能不能落地,第一步不是看代码,是确认构建环境。我见过太多人卡在“代码是对的但跑不起来”,问题十有八九出在套件选择或资源路径上。这一章先把工程骨架和构建链说清楚,再带你跑起第一个带 QML 的 Demo。这套源码在 Qt Creator 里打开后能看到两个“入口”:CMakeLists.txt 和 .pro 各有一套,我用的是 CMake 路线,qmake 路线留到 2.2 命令行里交代。
2.1 解压后先别急着双击:先看懂源码包目录结构
很多人的第一反应是双击 main.cpp,然后对着满屏报错发呆。正确做法是先看目录,因为它决定了你后面改代码时该去哪里找东西。常见的 Qt 实战源码包是按“界面、逻辑、资源、脚本”四个维度拆开的。
| 目录 / 文件 | 作用 | 你需要对它做什么 |
|---|---|---|
| src/main.cpp | 程序入口,创建窗口、加载引擎 | 基本不用动 |
| src/backend.h / backend.cpp | C++ 侧数据逻辑,QML 通过它拿数据 | 最核心的改造点 |
| qml/main.qml | 顶层界面,决定窗口里放什么 | 高频修改 |
| qml/Dashboard.qml | 仪表盘 / 曲线 / 列表等可复用组件 | 拷贝改造成自己的组件 |
| res/ | 图片、字体、样式表 | 换皮肤时替换 |
| CMakeLists.txt | CMake 构建脚本 | 新增 .cpp 或 .qml 时改这里 |
| tools/ | 批量替换、资源整理脚本 | 偶尔跑一下 |
这个结构不是随手分的。C++ 侧尽可能不碰界面,QML 侧不写业务算法,中间只通过注册类型和信号交互。你改 QML 颜色、布局、字号,不需要重新编译;你改 C++ 的取数逻辑,才需要点构建。源码包把这种边界画得很清楚,这也是它适合“实战”而非“教学片段”的原因。我第一次拿到这套包时,先不读代码,而是把每个目录下的文件列了一遍,记住哪些文件会在构建时被反复触碰——这一步能省下后面一半的排查时间。
2.2 套件怎么选:MinGW、MSVC 与 Qt Quick 运行时
源码包里有 CMakeLists.txt,也有 .pro 文件,说明作者同时照顾了两类用户。但无论用哪种构建脚本,第一步都必须先在 Qt Creator 里选对套件。我的建议是:如果你只是为了学 Qt、改 Demo,装 Qt 6 的 LTS 版本,套件选 MinGW 64-bit;如果你要接第三方库或者做 Windows 深度集成,才切到 MSVC。两者的差别在于编译器、运行时依赖和发布方式。
| 套件 | 编译器 | 发布依赖 | 适合场景 |
|---|---|---|---|
| MinGW 64-bit | GCC | windeployqt 打进去的 DLL | 学习、轻量工具、源码复现 |
| MSVC 2019 / 2022 | MSVC | VC++ 运行库 + windeployqt | 对接第三方库、深层系统调用 |
套件选完,命令行构建也值得掌握,因为源码包自带的 tools 脚本很可能就是用命令行的方式跑的。qmake 路线命令如下:
mkdir build && cd build qmake ../qt_quick_demo.pro "CONFIG+=release" mingw32-make -j4qmake 是 Qt 自带的构建工具,它读取 .pro 文件里的 SOURCES、HEADERS、DISTFILES 等字段,生成 makefile。CONFIG+=release会去掉调试库依赖,减少生成的程序体积,也避免发布时误带 debug 版 DLL。-j4是告诉编译器用 4 个线程并行编译,老电脑改成-j2会更稳定。
如果你选 CMake 路线,命令行则是这样:
mkdir build && cd build cmake .. -DCMAKE_PREFIX_PATH=C:/Qt/6.7.3/mingw_64 cmake --build . --config Release-DCMAKE_PREFIX_PATH必须指到你实际安装 Qt 的目录,少这一步 CMake 就找不到 Qt6Config.cmake,控制台直接报Could not find a package configuration file provided by "Qt6"。两个命令跑完,build 目录下会生成可执行文件,这一步通了,才轮到 QML 的事。
2.3 第一次把带 QML 的工程跑起来
源码包里最典型的工程是“混合工程”:窗口由 C++ 创建,界面塞 QML,数据从 C++ 经过注册类型流到界面。这种工程在 Qt 6 里需要同时链接 Widgets 和 Quick 两个模块,CMake 配置如下:
cmake_minimum_required(VERSION 3.16) project(quick_engine_demo) set(CMAKE_CXX_STANDARD 17) set(CMAKE_AUTOMOC ON) find_package(Qt6 COMPONENTS Widgets Quick REQUIRED) add_executable(quick_engine_demo src/main.cpp src/backend.cpp src/backend.h ) qt_add_resources(quick_engine_demo "qml" PREFIX "/" BASE "qml" FILES qml/main.qml qml/Dashboard.qml ) target_link_libraries(quick_engine_demo PRIVATE Qt6::Core Qt6::Gui Qt6::Quick Qt6::Widgets )这段配置里有几个参数必须看懂。CMAKE_AUTOMOC ON是处理 Q_OBJECT 类的前提,没有它,moc 不执行,程序会报“undefined reference to vtable”。qt_add_resources把 qml 目录下的文件编进二进制资源,PREFIX "/"表示资源根路径是根目录,BASE "qml"表示源文件从 qml 目录开始算。这样在代码里访问 QML 文件就是qrc:/main.qml,而不是写绝对路径。target_link_libraries里同时链接了 Widgets 和 Quick,这是混合工程的特征,别轻易删掉 Widgets,哪怕你暂时只弹了一个 QML 窗口,因为源码包的后续示例随时会用到 QFileDialog、QMessageBox 这类 Widgets 模块的类。
main.cpp 的入口写法也有一点讲究:
#include <QApplication> #include <QQmlApplicationEngine> #include "backend.h" int main(int argc, char *argv[]) { // 混合工程用 QApplication,纯 QML 示例才用 QGuiApplication QApplication app(argc, argv); // 把 C++ 类注册成 QML 类型,QML 里 import Demo.Backend 1.0 后直接用 qmlRegisterType<Backend>("Demo.Backend", 1, 0, "Backend"); QQmlApplicationEngine engine; // 从 qrc 加载而不是从本地文件加载,避免部署后路径失效 engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) { return -1; } return app.exec(); }这里qmlRegisterType的四个参数分别是:QML 侧 import 语句里的模块名、主版本号、次版本号、类型名。import Demo.Backend 1.0对应模块名Demo.Backend、主版本 1、次版本 0,QML 里写Backend {}对应类型名Backend。任何一个不匹配,QML 加载时都会提示is not a type。engine.rootObjects().isEmpty()是注册失败后的兜底检查,空就返回 -1,比让程序继续跑在一张白屏上要容易排查得多。第一次跑通了,说明这套源码包的环境已经没问题,接下来才进入真正“学 Qt 实战”的环节。
3. 读懂源码的核心链路:C++ 与 QML 桥接和三处可下手改造
很多人拿到源码包后先翻 QML,改颜色、改字号,玩得挺高兴,可一到“数据从哪儿来”就卡住。整套源码包的功力其实都在 C++ 与 QML 的桥接层上:界面是 QML 画的,数据是 C++ 给的,两者通过注册类型、属性绑定、信号这三个机制交互。把这条链路读透,你才算真正学 Qt,而不是学 QML 语法。
3.1 桥接层:为什么数据逻辑必须留在 C++
我见过不少学习者想把所有逻辑都写进 QML 的 JavaScript,图省事。短期看确实快,但一旦逻辑复杂,JS 调试、断点、类型检查都会变成灾难。源码包的思路是:QML 只负责“展示和交互”,C++ 负责“状态和算法”。backend.h 里是这样暴露接口的:
#ifndef BACKEND_H #define BACKEND_H #include <QObject> #include <QTimer> #include <QVariantList> class Backend : public QObject { Q_OBJECT // Q_PROPERTY 暴露属性:QML 里 backend.temperature 直接可读 Q_PROPERTY(double temperature READ temperature NOTIFY temperatureChanged) public: explicit Backend(QObject *parent = nullptr); // Q_INVOKABLE: 允许 QML 直接调用这个 C++ 方法 Q_INVOKABLE void startSimulation(); Q_INVOKABLE QVariantList snapshot(); double temperature() const; signals: // 属性变化信号:QML 绑定依赖它重新求值 void temperatureChanged(double value); private: double m_temperature = 0.0; QTimer m_timer; }; #endifQ_PROPERTY的声明格式是“类型 + 读取函数 + NOTIFY 信号”。QML 里写backend.temperature时,引擎调用temperature()拿到当前值;当 C++ 侧emit temperatureChanged(newValue)后,QML 里所有依赖这个属性的绑定表达式会自动重新求值。Q_INVOKABLE则是把 C++ 方法直接暴露给 QML 调用,不需要经过信号槽,对前端开发惯性的人来说更直观。
3.2 信号怎么从 C++ 发到 QML:模拟数据的完整闭环
属性绑定值不值得,要看信号发得对不对。backend.cpp 里用 QTimer 模拟实时数据,这是源码包演示“数据驱动界面”的标准手法:
#include "backend.h" #include <QRandomGenerator> Backend::Backend(QObject *parent) : QObject(parent) { // 每 1000ms 产生一个新温度值,发给 QML connect(&m_timer, &QTimer::timeout, this, [this]() { m_temperature = 20.0 + QRandomGenerator::global()->generateDouble() * 30.0; emit temperatureChanged(m_temperature); }); m_timer.setInterval(1000); } void Backend::startSimulation() { m_timer.start(); } QVariantList Backend::snapshot() { return { m_temperature, QString::number(m_temperature, 'f', 1) }; }这里的核心是emit temperatureChanged(m_temperature)。新手最容易漏掉这行,结果就是界面上的数字一动不动。QML 的属性绑定不是轮询,它只认 NOTIFY 信号,信号不发,绑定就不刷新。setInterval(1000)决定了数据刷新频率,想改成 500ms 就在这里调。换成真实数据源时,只需要把QRandomGenerator那行替换成串口读取或网络请求返回值,然后同样emit一次,QML 侧不用动一个字符。顺带提醒:Qt 5 源码包里没有QRandomGenerator,用qrand()是正常的,移植到 Qt 6 才需要替换。
QML 侧消费信号的写法有很多种,源码包里常见的是在组件里声明函数作为信号的处理器:
Item { id: root Backend { id: backend } Text { id: tempLabel anchors.centerIn: parent text: backend.temperature.toFixed(2) + " °C" } // 信号处理器:属性变化时同步打印,方便调试 Connections { target: backend function onTemperatureChanged(value) { console.log("最新温度:", value.toFixed(2)); } } }文字比较多的代码
正确处理:源码是 UTF-8 without BOM,或者统一切到 GBK,但别混着来。
4.3 现象:运行时系统提醒 “module QtQuick is not installed” 或者 Not created. 程序崩溃
原因:套件错位。如果你用 MinGW 套件编译,运行用的 Qt 库和 QML 插件必须也来自 MinGW 目录;MSVC 同理。两边混用,QML 引擎找不到对应架构的插件,直接崩溃。解决:在 Qt Creator 里检查“构建套件”和“运行环境”是否属于同一目录。命令行运行时,把 Qt 目录下的bin加进 PATH,并设置QML2_IMPORT_PATH指向qml目录:
export QML2_IMPORT_PATH=C:/Qt/6.7.3/mingw_64/qml4.4 现象:界面能显示,但拖动窗口时白屏、响应迟缓
原因:在 UI 线程里做了耗时操作。最经典的就是把snapshot()里写大循环,或者在主线程里等串口超时。QML 的渲染线程和主线程绑定,同一组数据样本,延时越大越卡。解决:耗时逻辑放到QThread或QtConcurrent里跑,完成后通过信号回发。像是把串口读取从Backend::startSimulation()里拆出来,单独在工作线程里串数据,这也正是后来项目里把 block 操作全部清掉的原因。
4.5 现象:把可执行文件拷到别的电脑,双击没反应、黑屏、缺 DLL
原因:没有打包运行库和 QML 插件。Qt 的发布不是把 exe 拷走就完事,还需要一堆 DLL、插件、qmldir 文件。处理:用官方部署工具。Windows 上跑:
cd build windeployqt --qmldir ../qml release/quick_engine_demo.exe--qmldir告诉 windeployqt 去扫描 qml 目录下的 import 依赖,把相关插件全部拷到 exe 旁边。这一步做得越早越好,不要等代码全部写完再补救,否则你会面对几十个“找不到 DLL”的弹窗。
这一章有个核心体验:确实。源码包里的报错,一大半是路径和配置问题,代码本身反而是最可靠的部分。排查的时候先看环境,再看代码,能省下不少内耗。
5. 把源码包改造成自己的第一个跨平台小工具:从拷贝到验证
我拿到这套源码之后做的第一件事不是读,而是复制出一个最小工程,把它改成自己的小工具。具体做法:把quick_engine_demo目录整体复制一份,改名为my_alert_tool,然后把backend类重命名为AlertEngine,连带的 CMake 文件名、头文件 include guard、QML import 名称一并替换。这里最怕的是 QML 类型名“撞车”:如果重命名后还保留Backend这个 QML 类型名,Qt Quick 的缓存会让人怀疑是不是没改干净。所以我一般会全局搜Backend、quick_engine_demo,逐个替换,别让 IDE 的“批量重命名”背锅。
改完代码后,验证分两步走。第一步是在 Qt Creator 里跑通,这时看到自己的窗口、自己的数据、自己的颜色,算是“能用”。第二步才是关键:模拟用户环境。把 Release 构建产物放到一台没有装 Qt 的干净机器上,用windeployqt部署完,双击运行,确认没有缺 DLL、没有黑屏。这个验证路径比任何 code review 都严格。我现在的习惯是:拿到任何 Qt 或 Qt Quick 源码包,先花 20 分钟过一遍“拷目录 → 全局替换 → 跑通 → 部署 → 干净机器验证”这条流水线,不跑完这套流程,代码不看。从那以后,再没遇到过“代码是对的但发布就是不行”的翻车现场。希望你在复现这套源码时,也把这条验证链走一遍,它会帮你避开大多数 Qt 实战里的暗坑,省下的时间足够再学一个模块。
本文还有配套的精品资源,点击获取