资讯动态

Qt5 学习路线:从 79 章补全到 94 章实战避坑指南

发布时间:2026/10/14 3:51:26 来源:尧图企业网站定制
简介这份《QT学习之路2-2020整理版》面向希望系统掌握Qt框架的开发者从零基础入门到进阶实战均可参考。内容基于豆子博客整理在原有79章基础上补充了2016年后更新的15章共94章涵盖Qt简介、信号槽机制、自定义信号槽、模块划分、MainWindow、资源文件、对象模型、布局管理器、菜单栏工具栏状态栏、对话框数据传递以及多线程、国际化、数据库与网络编程等高级主题并涉及Qt Creator使用与调试技巧。资源为1个PDF文件压缩包约5.79MB单文件结构便于通读与检索。目前已有869人学习适合按章节顺序研读也可作为日常开发中查阅信号槽、事件处理与界面布局等知识点的案头参考。1. 从 79 章到 94 章这份 445 页的 Qt 学习之路 2 整理版到底补了什么网上搜 Qt 教程十篇有八篇指向同一个源头但版本参差不齐。最常见的情况是你找到一份 PDF翻到第 79 章 QML 组件就没了后面全是空白或者重复的旧内容。这份 2020 年整理版把豆子 2016 年 3 月之后更新的 80 到 94 章补了进来凑齐 94 章、445 页从 Qt 简介一路讲到 QML 和 QtQuick 2。它适合两类人一是刚接触 Qt5 和 QtCreator、需要一条完整学习路径的新手二是早年看过前 79 章、想补齐后续内容的熟手。整份资料基于 Qt4 与 Qt5 并行介绍的思路信号槽、事件系统、model/view、多线程、网络、XML/JSON 都有独立章节不是那种只讲拖控件的速成手册。2. 环境搭建与第一个 Qt5 工程从 Qt SDK 选型到 Hello World 跑通2.1 Qt SDK、Qt Library、Qt Creator 到底装哪个资料第 3 章把安装选项拆得很清楚但放到今天的环境里有些细节需要按实际情况调整。原文列了三个下载项Qt SDK 是完整开发环境包含库、IDE 和工具链Windows 平台大约 1.7GQt Library 只有库适合已经装好环境、只想升级库版本的情况Qt Creator 是那个轻量级 IDE原文说最新版是 2.5.2现在版本号早就翻了几番但用法逻辑没变。我一般建议直接装 Qt SDK 或者用官方在线安装器勾选 Qt Creator 加对应版本的 Qt 库。原文特别提醒了一点Qt SDK 里自带的 mingw 编译器版本往往偏旧比如当时 SDK 1.2.1 里带的是 mingw 4.4而编译 Qt5 程序需要 gcc 4.5 以上。这个坑现在依然存在只是表现形式变了——你装的 Qt 版本和编译器版本不匹配时qmake 能跑但 make 到一半报一堆链接错误。提示如果你在 Windows 上做 Qt5 开发优先用官方安装器里配套的 mingw 或 MSVC 套件不要自己混搭版本。原文提到在 Windows 上编译 Qt5 源码需要 perl、python、git 三件套并且要配好路径这个操作在 Linux 上简单得多原文作者用的是 openSUSE。2.2 用 Qt Creator 建第一个工程并跑通原文第 3 章 Hello World 部分给的是 Qt4 时代的步骤但 Qt Creator 的操作逻辑基本没变。下面按 Qt5 工程的实际流程走一遍。# 假设你已经装好 Qt Creator 和对应 Qt 版本 # 打开 Qt Creator - File - New File or Project # 选择 Application (Qt) - Qt Widgets Application # 工程名填 HelloQt路径不要有中文和空格 # Build system 选 qmake原文时代只有 qmake现在 CMake 也常用 # 基类选 QWidget取消勾选 Generate form工程建好后Qt Creator 会自动生成main.cpp、hellowqt.cpp、hellowqt.h和.pro文件。打开hellowqt.cpp把构造函数改成#include hellowqt.h #include QLabel HelloQt::HelloQt(QWidget *parent) : QWidget(parent) { // 创建一个 QLabel父对象设为当前窗口 QLabel *label new QLabel(tr(Hello, Qt5!), this); // 设置标签位置和大小 label-setGeometry(50, 50, 200, 30); // 设置窗口标题和初始大小 setWindowTitle(tr(Hello Qt)); resize(300, 150); } HelloQt::~HelloQt() { }这段代码的逻辑很直白QLabel是 Qt 里最基础的显示控件构造时传入this作为父对象这样 label 的生命周期就挂到了窗口上窗口销毁时 label 自动释放不需要手动 delete。setGeometry设置的是相对父窗口的坐标和尺寸前两个参数是 x、y后两个是宽、高。tr()是 Qt 的国际化翻译函数即使现在不做多语言养成习惯后面省事。.pro文件里确认有QT widgetsQt5 把 widgets 拆成了独立模块Qt4 时代不需要这一行。点左下角绿色三角编译运行如果弹出一个小窗口显示 “Hello, Qt5!”环境就算通了。2.3 编译报错时先查这三处新手在这一步翻车的概率不低常见现象和排查顺序如下。第一报 “cannot find -lQt5Widgets”说明.pro里没加QT widgets或者 Qt 版本选错了套件。第二报 “undefined reference tovtable for HelloQt”通常是类声明里加了Q_OBJECT宏但没执行 qmakeQt Creator 里右键工程选 Run qmake 再重新构建。第三窗口一闪而过或者根本不弹检查main.cpp里是不是写了return app.exec();没有这行事件循环起不来。3. 信号槽与自定义信号从 connect 写法到 Qt5 新语法3.1 信号槽的基本连接方式和参数匹配规则信号槽是 Qt 区别于其他 C 框架的核心机制原文第 4 章和第 5 章用了大量篇幅讲这个。简单说信号是对象状态变化时发出的通知槽是接收通知后执行的函数。连接用QObject::connectQt4 和 Qt5 的写法有区别。// Qt4 旧写法SIGNAL/SLOT 宏字符串匹配编译期不检查 connect(sender, SIGNAL(valueChanged(int)), receiver, SLOT(updateValue(int))); // Qt5 新写法函数指针编译期检查参数类型 connect(sender, QSlider::valueChanged, this, MyWidget::updateValue); // 带参数的 lambda 写法适合简单逻辑 connect(button, QPushButton::clicked, this, [this]() { label-setText(按钮被点击了); });Qt5 新语法的优势在于编译期就能发现信号和槽参数不匹配的问题而 Qt4 的宏写法要等到运行时才报 “No such signal” 或者 “No such slot”。原文第 16 章专门讲了 Qt5 信号槽新语法核心变化就是去掉了宏改用函数指针。参数匹配规则是槽的参数个数可以少于信号但类型必须能隐式转换反过来不行。3.2 自定义信号槽的完整流程原文第 5 章给了一个自定义信号槽的例子这里按 Qt5 的写法重新走一遍。假设有一个Counter类内部计数到 10 时发出信号外部接收后打印。// counter.h #ifndef COUNTER_H #define COUNTER_H #include QObject class Counter : public QObject { Q_OBJECT public: explicit Counter(QObject *parent nullptr); void startCounting(); signals: // 自定义信号计数达到阈值时发出携带当前值 void thresholdReached(int value); private: int m_count; }; #endif // COUNTER_H// counter.cpp #include counter.h #include QDebug Counter::Counter(QObject *parent) : QObject(parent), m_count(0) { } void Counter::startCounting() { for (int i 1; i 10; i) { m_count i; if (m_count 10) { // 发出信号参数为当前计数值 emit thresholdReached(m_count); } } }// main.cpp #include QCoreApplication #include counter.h #include QDebug int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); Counter counter; // 连接自定义信号到 lambda 槽 QObject::connect(counter, Counter::thresholdReached, [](int value) { qDebug() 阈值到达当前值 value; }); counter.startCounting(); return app.exec(); }几个关键点signals:是 Qt 的宏展开后是public所以信号函数本质上是 public 的但你不应该直接调用它只能用emit触发。信号只需要声明不需要实现moc 会自动生成。emit也是个宏展开后是空纯粹为了代码可读性。自定义信号和槽的类必须在头文件里加Q_OBJECT宏否则 moc 不会处理连接时会报 “No such signal”。3.3 连接失败时的排查清单信号槽连接不上的现象通常是编译通过运行时输出 “QObject::connect: No such signal” 或者 “No such slot”槽函数不执行。排查顺序第一检查类声明里有没有Q_OBJECT宏加了之后有没有重新执行 qmake。第二Qt4 宏写法下检查信号和槽的签名是否完全一致包括参数类型和 const 修饰。第三跨线程连接时检查连接类型默认AutoConnection在跨线程时会变成队列连接槽函数在接收者线程执行如果接收者线程没有事件循环槽永远不会被调用。第四信号或槽被重载时Qt5 函数指针写法需要显式转换比如static_castvoid(QComboBox::*)(int)(QComboBox::currentIndexChanged)。4. 事件系统与绘制从 event() 到 Graphics View Framework4.1 事件的接受与忽略机制原文第 18 到 23 章集中讲事件系统这是 Qt 里比信号槽更底层的一套机制。信号槽是对象间的通信事件是窗口系统发给应用程序的通知。鼠标点击、键盘按下、窗口重绘都是事件。Qt 里所有事件都继承自QEvent处理入口是QObject::event()函数具体类型的事件再分发给mousePressEvent()、keyPressEvent()这些虚函数。事件的接受与忽略是原文第 19 章的重点。默认情况下事件处理函数调用event-accept()表示事件已处理不会再向上传播调用event-ignore()表示忽略事件会继续传给父控件。这个机制在自定义控件里很关键——如果你在子控件里忽略了一个鼠标事件父控件才有机会处理它。// 自定义按钮只处理左键点击右键点击忽略后传给父窗口 void MyButton::mousePressEvent(QMouseEvent *event) { if (event-button() Qt::LeftButton) { qDebug() 左键点击自己处理; event-accept(); } else { // 右键点击不处理交给父控件 event-ignore(); } }4.2 事件过滤器与自定义事件事件过滤器允许一个对象监视另一个对象的事件不用继承就能拦截。用法是给目标对象安装过滤器然后在过滤器的eventFilter()函数里处理。// 在 Widget 构造函数里给 lineEdit 安装事件过滤器 lineEdit-installEventFilter(this); // 实现 eventFilter bool Widget::eventFilter(QObject *watched, QEvent *event) { if (watched lineEdit event-type() QEvent::KeyPress) { QKeyEvent *keyEvent static_castQKeyEvent *(event); if (keyEvent-key() Qt::Key_Enter || keyEvent-key() Qt::Key_Return) { qDebug() 回车被拦截不传给 lineEdit; return true; // 返回 true 表示事件已处理不再分发 } } // 其他情况交给基类处理 return QWidget::eventFilter(watched, event); }自定义事件的流程是定义一个继承自QEvent的类注册事件类型然后用QCoreApplication::postEvent()或sendEvent()发送。原文第 23 章有完整示例核心是QEvent::registerEventType()获取一个不冲突的类型值。4.3 Graphics View Framework 的坐标与场景原文第 30 章介绍了 Graphics View Framework这是 Qt 里做复杂 2D 绘图的框架贪吃蛇游戏那几章就是用它实现的。核心三件套是QGraphicsScene场景、QGraphicsView视图、QGraphicsItem图元。场景管理所有图元视图负责显示图元是场景里的基本元素。坐标系统有三层图元坐标、场景坐标、视图坐标。图元坐标是相对于父图元的场景坐标是绝对的视图坐标是窗口像素坐标。转换用mapToScene()、mapFromScene()、mapToParent()这些函数。原文第 28 章专门讲了坐标系统这块不搞清楚图元定位会各种偏移。// 创建一个场景和视图 QGraphicsScene *scene new QGraphicsScene(this); QGraphicsView *view new QGraphicsView(scene, this); // 添加一个矩形图元位置在场景坐标 (100, 100) QGraphicsRectItem *rect scene-addRect(0, 0, 50, 50); rect-setPos(100, 100); // 视图设置抗锯齿和渲染提示 view-setRenderHint(QPainter::Antialiasing); view-setSceneRect(0, 0, 800, 600);贪吃蛇游戏那四章第 31 到 34 章是 Graphics View 的实战把蛇身每个节点做成图元定时器驱动移动碰撞检测判断是否吃到食物或撞墙。这个例子值得跟着敲一遍比单纯看文档理解深得多。5. 避坑与常见问题线程、模型视图、文件读写的翻车记录5.1 线程里直接操作 UI 导致程序崩溃现象在QThread::run()里调用ui-label-setText()程序随机崩溃或者界面卡死。原因Qt 的 UI 对象只能在主线程操作子线程直接调用会触发未定义行为。解决用信号槽跨线程通信子线程发信号主线程的槽函数更新 UI。连接类型用默认的AutoConnection即可Qt 会自动排队到主线程事件循环。// 子线程类 class Worker : public QObject { Q_OBJECT public slots: void doWork() { // 耗时操作 emit resultReady(完成); } signals: void resultReady(const QString result); }; // 主线程里 QThread *thread new QThread; Worker *worker new Worker; worker-moveToThread(thread); connect(thread, QThread::started, worker, Worker::doWork); connect(worker, Worker::resultReady, this, MainWindow::handleResult); thread-start();5.2 模型视图里数据改了但视图不刷新现象自定义模型里修改了底层数据调用setData()返回 true但视图显示没变。原因setData()里没有发出dataChanged()信号视图不知道数据变了。解决在修改数据后发出emit dataChanged(index, index, {Qt::DisplayRole});角色列表按实际修改的角色填。如果是插入或删除行必须用beginInsertRows()/endInsertRows()包裹否则视图和模型的行数会对不上直接崩溃。5.3 文本文件读写中文乱码现象用QFile加QTextStream读中文文本读出来是乱码。原因Qt5 默认用 UTF-8 编码但 Windows 上很多文本文件是 GBK 编码。解决显式设置编码stream.setCodec(GBK);或者用QTextCodec::codecForName(GBK)。写文件时同理不设置的话默认 UTF-8用记事本打开可能乱码。原文第 37 章讲文本文件读写时提过编码问题但当时 Qt4 的默认编码行为和 Qt5 不同这个坑在 Qt5 里更常见。5.4 信号槽连接了但槽函数不执行现象connect返回 true编译运行都没报错但槽函数就是不执行。原因有好几种接收者对象在连接之后被销毁了Qt 会自动断开连接跨线程连接时接收者线程没有事件循环信号被阻塞了QObject::blockSignals(true)没恢复或者槽函数被声明为 private 但用了 Qt4 宏写法moc 找不到。解决先确认接收者对象还活着再检查线程事件循环最后用QObject::receivers()查一下连接数。5.5 QML 和 C 混合时上下文属性找不到现象QML 文件里用contextProperty注册的 C 对象运行时报 “ReferenceError: xxx is not defined”。原因注册时机不对rootContext()-setContextProperty()必须在engine.load()之前调用。解决把注册代码放到QQmlApplicationEngine加载 QML 之前。另外如果用了qmlRegisterType注册类型QML 里需要import对应的模块和版本号版本号不匹配也会找不到。6. 把 94 章拆成可执行的练习计划我的三阶段推进法这份资料 445 页、94 章从头到尾读一遍很容易前面忘后面。我自己的做法是拆成三个阶段每个阶段配一个能跑起来的小项目不追求看完追求跑通。第一阶段是基础篇覆盖第 1 到 30 章重点是信号槽、事件、绘制系统。练习项目做一个带菜单栏、工具栏、状态栏的文本编辑器能打开保存文件、能改字体颜色、能弹对话框。这个项目把 MainWindow、动作、资源文件、对话框、文件读写、事件处理全串起来了。做完这个Qt 的 GUI 编程套路基本就熟了。第二阶段是进阶篇覆盖第 35 到 75 章重点是文件、容器、model/view、数据库、XML/JSON、网络、多线程。练习项目做一个简易的本地数据管理器用 SQLite 存数据用QTableView加自定义模型显示支持增删改查和排序过滤数据能导出成 JSON。这个项目把 model/view 和数据库操作绑在一起练比单独看章节效果好得多。第三阶段是 QML 篇覆盖第 76 到 94 章重点是 QML 语法、基本元素、组件、和 C 的交互。练习项目做一个简单的待办事项应用界面用 QML 写数据逻辑用 C 写通过信号槽和上下文属性通信。这个项目跑通Qt Quick 的混合开发流程就清楚了。每个阶段的时间分配大概是基础篇两周进阶篇三周QML 篇两周。不用赶进度每个练习项目跑通再往下走。原文第 1 章序言里豆子说这个系列定位是入门教程穿插了一些实现层面的内容但不深入。我的体会是入门阶段最怕的是只看不练信号槽、事件、model/view 这三块不亲手写一遍代码光看文字很难真正理解。从那以后我每次带新人都强制要求先跑通一个最小可运行工程再去看理论章节不然看多少章都是黑匣子。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑