Qt5 C++ 目录版PDF实战:GUI、数据库与打包避坑
简介:这份PDF文档是面向C++开发者整理的Qt5实战范例合集,以完整目录版形式覆盖从环境搭建到应用发布的全流程,适合刚接触Qt框架、需要按知识点查漏补缺的初中级开发者对照练习。全书共十四章,开篇讲解Qt简介、Qt Creator安装与Hello World入门,随后依次展开窗体创建与无边框移动、按钮标签文本编辑等常用控件、日历与登录窗口等组件、文件读写、图形图像处理、多媒体、系统与注册表操作、数据库与网络开发、进程线程及数据安全,末章给出可执行文件打包与多平台部署思路。资源包仅含1个pdf文件,体积约2.84MB,章节条目细化到具体控件类名与示例编号,方便按目录快速定位。目前已有2459人学习下载,可作为Qt5桌面应用开发过程中的速查手册与练习索引。
1. 只有目录的 PDF,反而更适合当案头手册
下载页面上摆着的是一份“完整目录版”PDF,翻开不是成段讲解,而是十四章节的范例清单:第 1 章开发环境,第 2 章窗体应用,第 3 章控件应用,一路排到第 14 章打包部署,中间夹着文件操作、图形图像、多媒体、系统操作、注册表、数据库、网络、进程与线程、数据安全。第一次翻会有点失落——条目多,正文薄。但做 Qt5 C++ 这行久了会发现,这种结构的价值恰恰不在“读完”,在“按索引取用”:手上有个老项目要改标题栏图标、要拖一个无边框窗口、要往 INI 里塞配置、要连一个 SQLite 库,翻到对应条目,照着参数改,十分钟能见效。
它适合三类人:维护存量 Qt5 工程的开发者、需要快速补齐 GUI 这一环的后端或嵌入式工程师、以及带学生做课程设计时需要一份“能力边界清单”的人。真正反直觉的地方是,目录本身就是需求池,你先看目录能干什么,再决定要不要啃正文,比从头读到尾省太多时间。
2. 环境与工程骨架:把 Qt5 + MinGW 这套组合先跑通
翻这份手册第一个拦路虎不是代码,是环境。原书基于 Qt 5.1.1 的 32 位 MinGW 版本,这个组合放到今天依然能跑,但构建套件、编码、插件路径三件事没对齐,后面所有范例都是空谈。我的习惯是先把一个空工程编译成功、能弹出窗口、能改标题,再去碰第 2 章往后的东西,不然出了问题分不清是环境还是代码。
2.1 三基类怎么选:QMainWindow、QWidget、QDialog 的分工
手册第 2 章第一节就点到了三个基类,很多人扫一眼就跳过,结果写到一半发现主窗口没有状态栏、或者对话框关不掉。这三个类的边界其实很清楚:
| 基类 | 典型场景 | 需要注意 |
|---|---|---|
| QMainWindow | 带菜单栏、工具栏、状态栏的主窗口 | 中央部件要用 setCentralWidget 挂上去,直接 addWidget 不生效 |
| QWidget | 自定义控件、无边框窗口、嵌入式面板 | 最轻,什么都要自己搭,包括关闭按钮 |
| QDialog | 登录框、设置页、短任务交互 | 有 exec() 模态循环和返回值,别拿它当主窗口 |
选型判断我一般用一句话:这个窗口需不需要常驻、需不需要菜单栏。需要常驻且有菜单,QMainWindow;只是弹一下拿个结果,QDialog;剩下的全用 QWidget。手册里“登录窗口”那节用的是对话框思路,“移动无边框窗体”用的是 QWidget 思路,两个范例放在一起看,选型逻辑就出来了。
有一个容易踩的点:QMainWindow 的布局不能直接 setLayout,必须 new 一个中央部件再 setCentralWidget。新手在这里卡住的现象是窗口一片空白,代码没报错,怎么也找不到原因。
2.2 一个最小可复现工程:main.cpp 与 .pro 骨架
不管后面复现哪个范例,工程骨架都长一个样。下面这三段是每次新建项目时我都会核对一遍的,尤其是 .pro,它决定了你能不能用到 sql、network、multimedia 这些模块。
// main.cpp —— 程序入口,顺序不能乱 #include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); // 必须先构造,后面所有 QWidget 都依赖它 MainWindow w; // 构造主窗口,此时还没显示 w.show(); // 真正映射到屏幕 return a.exec(); // 进入事件循环,不写这行窗口一闪就没 }// mainwindow.cpp —— 第 2 章所有窗体范例的公共起点 #include "mainwindow.h" #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { this->setWindowTitle("Qt5 窗体应用"); // 标题,后面改图标也挂在这 this->resize(400, 300); // 初始尺寸,别和最大最小尺寸混用 QLabel *label = new QLabel(this); label->setText("Hello World"); label->setGeometry(100, 80, 120, 30); // x, y, 宽, 高,相对父窗口 }# QtDemo.pro —— 模块声明,缺一个就链接不过 QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets QT += sql network multimedia # 按手册章节需要逐行放开 TARGET = QtDemo TEMPLATE = app SOURCES += main.cpp mainwindow.cpp HEADERS += mainwindow.h RESOURCES += res.qrc # 图标、图片资源,第 2 章就要用三段代码对应三个职责:main.cpp 管生命周期,mainwindow.cpp 管外观,.pro 管依赖。参数上要留意 setGeometry 的四个值,前两个是相对父窗口的坐标,后两个是宽高;如果父窗口是顶层窗口,那前两个就约等于屏幕坐标。resize 只改尺寸不改位置,和手册第 2 章“控制窗体大小”里的 setMaximumSize/setMinimumSize 是两回事——前者是建议值,后者是硬约束,用户拖不动。
.pro 里那行 greaterThan 是给 Qt4 兼容留的,如果你确定只用 Qt5,直接写 QT += widgets 也行。但保留它没坏处,某些旧构建环境会因此少报一个错。
2.3 编译不过时先看这三处
构建失败九成出在三个地方,按这个顺序排,比乱改代码快。
第一是构建套件。工具里同时装了两三个 Qt 版本时,很容易出现 qmake 指向 A 版本、编译器指向 B 版本的情况,报错往往是找不到 QApplication 头文件,或者“Qt version is not properly installed”。处理办法是打开构建和运行设置,把套件里的 qmake 路径、编译器路径、调试器路径三行逐一核对,然后删掉构建目录重新执行 qmake。血泪经验是别在原目录上反复点重新构建,增量缓存会把错误藏起来。
第二是 .pro 的模块声明。手册第 10 章数据库、第 11 章网络、第 7 章多媒体,分别要 QT += sql、network、multimedia。漏写的表现不是编译错误,而是链接期报一堆 undefined reference,或者在运行期发现 QSqlDatabase::drivers() 返回空列表。这种错最难查,因为代码本身没问题。
第三是源文件编码。老版本 MinGW 对不带 BOM 的 UTF-8 中文注释容忍度还行,但一旦换到 MSVC 套件,中文注释和字符串就可能报 C2001 或直接乱码。我的做法是所有 .cpp/.h 统一存成 UTF-8 with BOM,字符串里出现中文就用 QString::fromUtf8 包一层,省得后面来回折腾。
3. 窗体与控件:把第 2、3 章的范例复现成能跑的代码
第 2 章和第 3 章是这份手册密度最高的部分,窗体 8 个范例、控件 22 个范例,基本上把日常 GUI 会碰到的东西铺全了。这两章不建议通读,建议按“我要做的界面长什么样”倒着挑。下面挑四个使用频率最高的,把参数和坑说透。
3.1 窗体尺寸、位置、背景色:三个函数定死外观
手册里“控制窗体大小”“窗体初始位置及背景色”是两个独立条目,实际项目里通常要一起写。
// 固定 300x300,并在屏幕 (100,100) 处显示,背景红 this->setWindowTitle("Qt5 窗体应用"); this->setMaximumSize(300, 300); // 上限 this->setMinimumSize(300, 300); // 下限,和上限相等即锁定 this->move(100, 100); // 相对屏幕左上角 this->setStyleSheet("background:red;");setMaximumSize 和 setMinimumSize 同时设成一样,窗口就彻底拖不动了,这是最省事的锁尺寸写法。只设其中一个的话,用户仍然能在另一个方向上拉伸,很多“为什么还能拉大”的问题都出在这。
move 的参数是相对屏幕的,但如果这个窗口有父窗口,move 就变成相对父窗口,位置会整体偏移。判断方法很简单:看构造函数的 parent 参数是不是 nullptr。
setStyleSheet 这一行要小心,它会把样式递归作用到所有子控件上。给主窗口设 background:red 之后,里面的 QLabel、QPushButton 背景也可能被染红,看起来像控件坏了。规范做法是只给需要背景的那个控件设,或者在样式里加类名限定,比如QMainWindow { background:red; }。这一点手册的范例没展开,但实际用起来几乎必踩。
3.2 无边框窗体拖动:坐标记账和一个易漏的细节
“移动无边框窗体”是第 2 章里信息量最大的一个范例,它同时涉及窗口标志、鼠标事件、信号槽三块内容。
// mainwindow.h 里声明 protected: void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; private: QPoint last; // 记录上一次的全局鼠标位置// 构造函数里去掉标题栏,并补一个关闭按钮 this->setWindowFlags(Qt::FramelessWindowHint); QPushButton *btClose = new QPushButton(this); btClose->setText("关闭"); connect(btClose, &QPushButton::clicked, this, &QWidget::close);// 三个鼠标事件,核心是全局坐标的差值 void MainWindow::mousePressEvent(QMouseEvent *e) { last = e->globalPos(); // globalPos 是屏幕坐标,不是窗口内坐标 } void MainWindow::mouseMoveEvent(QMouseEvent *e) { int dx = e->globalX() - last.x(); // 鼠标这一帧移动了多少 int dy = e->globalY() - last.y(); last = e->globalPos(); // 先算差再更新,顺序反了窗口会抖 move(x() + dx, y() + dy); // x()/y() 是窗口左上角的全局坐标 } void MainWindow::mouseReleaseEvent(QMouseEvent *e) { Q_UNUSED(e); // 释放时不需要再算一次,移动已经处理完 }这段代码的关键在坐标体系。globalPos 拿的是鼠标在屏幕上的绝对位置,x() 和 y() 拿的是窗口左上角在屏幕上的位置,两者相减得到的 dx、dy 就是窗口该平移的量。手册原版在 mouseReleaseEvent 里又算了一遍 dx、dy 并 move 了一次,功能上没错,但会多一次无效移动,窗口边缘偶尔会跳一下。我一般把释放事件留空,只保留按下和移动两个。
另一个隐蔽的坑:如果这个无边框窗口是作为子窗口嵌在另一个窗口里的,x() 和 y() 就变成相对父窗口的值,此时 move 的基准错了,窗口会越拖越偏。判断标准还是看 parent 是不是空。遇到拖不动的现象,先确认 setWindowFlags 是不是在 show() 之前调的——放在 show() 之后调,窗口会先闪一下再变无边框。
还有一点,Qt 5 里 e->globalPos() 返回 QPoint,可以直接赋值;到更高版本这套 API 被标记弃用,改成 globalPosition().toPoint()。手册基于 Qt 5,照抄没问题,但如果你在同一套代码里混用不同版本的 Qt,这里编译会报警告。
3.3 资源文件与标题栏图标:.qrc 的三段式路径
“修改标题栏图标”这个范例,难的不是 setWindowIcon,是资源文件怎么建。手册给的路径是:/new/prefix1/ico,这个字符串由三段拼起来:冒号代表资源系统,new/prefix1 是前缀,ico 是别名。
<!-- res.qrc 的最终形态,prefix 和 alias 都能自定义 --> <RCC> <qresource prefix="/icons"> <file alias="app">resource/app.ico</file> </qresource> </RCC>// 前缀 /icons + 别名 app,拼成 :/icons/app this->setWindowIcon(QIcon(":/icons/app"));参数说明:prefix 是逻辑分组,多个目录的资源可以共用同一个前缀;alias 是省略路径的简写,没有别名就得写完整相对路径,形如:/icons/resource/app.ico。图标建议用 16x16 或 32x32 的 ICO,PNG 也能加载,但在部分系统的任务栏上显示会发虚。
最常见的翻车是改完 qrc 或换了图片,运行起来图标还是旧的。原因是 rcc 生成的结果没重新编译,增量构建没识别到资源变化。解决方式是构建菜单里先执行一次 qmake,再清理重编译。这个操作我后来养成习惯,凡是动了 .qrc、.pro、图片,一律先 qmake。
3.4 控件与布局:按参数表配 QGridLayout
第 3 章 22 个控件范例,逐个抄没意义,真正要建立的是“控件 + 布局”的组合感。手册里 QHBoxLayout 和 QGridLayout 两节挨着放,正好对照着看。
QGridLayout *grid = new QGridLayout; grid->addWidget(new QLabel("用户名"), 0, 0); // 第 0 行第 0 列 grid->addWidget(new QLineEdit, 0, 1); grid->addWidget(new QLabel("密码"), 1, 0); grid->addWidget(new QLineEdit, 1, 1); grid->addWidget(new QPushButton("登录"), 2, 0, 1, 2); // 跨 1 行 2 列 grid->setColumnStretch(1, 1); // 第 1 列吃掉多余宽度 grid->setSpacing(8); // 控件间距addWidget 的五个参数依次是控件、行、列、跨行数、跨列数,后两个省略时默认 1。setColumnStretch 是控制拉伸比例的关键,不设的话窗口拉大时所有列等宽增长,输入框会变得很怪。setSpacing 控制间距,setContentsMargins 控制布局到父窗口的留白,这两个经常被混用,实际效果差挺多。
有个使用习惯值得记:布局不要一层套一层套太深。我有次做一个设置页套了四层 QVBoxLayout 里嵌 QHBoxLayout 再嵌 QGridLayout,界面能显示,但 resize 的时候卡顿明显。后来压平到两层,问题就消失了。
4. 文件、图形、数据:第 5 到第 10 章怎么落地
中段这几章是这份手册的分水岭,前面的窗体和控件属于“看得见”,从这里开始进入“用得着”。文件操作、图形图像、数据库这三块,几乎每个实际项目都会碰到至少两块,而它们的坑又都不在代码本身,在选型和路径上。
4.1 QFile、QSettings、QDomDocument:三条不同的存储路径
手册第 5 章把创建文件夹、写入文件、INI 读写、XML 读写放在一起,看起来是并列关系,实际上对应三种完全不同的场景,选错会给自己埋雷。
| 方式 | 适用数据 | 特点 |
|---|---|---|
| QFile + QTextStream | 日志、导出文本、自定义格式 | 完全自己管格式,灵活但解析要手写 |
| QSettings | 配置项、窗口状态、用户偏好 | 键值结构,跨平台自动选存储位置 |
| QDomDocument | 层级数据、与外部系统交换 | 结构清晰,解析开销比前两者大 |
写配置我基本只用 QSettings,一行能省掉路径判断:
// 显式指定 IniFormat,文件就会落在当前目录,而不是系统注册表 QSettings cfg("config.ini", QSettings::IniFormat); cfg.setValue("db/host", "127.0.0.1"); cfg.setValue("db/port", 3306); cfg.sync(); // 立即落盘,别指望析构时一定写 QString host = cfg.value("db/host", "localhost").toString(); // 第二个参数是默认值 int port = cfg.value("db/port", 0).toInt();参数上有个细节:QSettings 不指定 IniFormat 时,在 Windows 上默认写注册表,很多“配置文件找不到”的困惑就来自这里。手册里 INI 那一节其实有提,但容易看漏。另外 value() 的返回类型是 QVariant,取的时候一定要带类型转换和默认值,不带默认值遇到缺失键会返回空 QVariant,转 int 得到 0,转 QString 得到空串,出错时毫无提示。
XML 这块,QDomDocument 的读取流程是:打开文件、setContent、拿 documentElement、逐层 firstChildElement 往下走。节点遍历时要用 nextSiblingElement 而不是 nextSibling,后者会把空白文本节点也当兄弟节点,循环次数会翻倍。
4.2 QPainter 图像特效:调用顺序决定结果
第 6 章从绘制文字线条一路排到模糊、着色、阴影、透明,14 个范例。这一章的价值在于它把 QPainter 的变换系统和 QImage 的像素操作串起来了,但手册按“效果”分节,容易让人以为它们是并列的独立功能,实际上很多效果要靠变换顺序堆出来。
QImage canvas(200, 200, QImage::Format_ARGB32); canvas.fill(Qt::transparent); QPainter p(&canvas); p.setRenderHint(QPainter::Antialiasing, true); // 抗锯齿,画线画圆必开 p.setRenderHint(QPainter::SmoothPixmapTransform, true); // 缩放图片不失真 p.translate(100, 100); // 把原点挪到画布中心 p.rotate(30); // 顺时针 30 度,y 轴向下所以正角是顺时针 p.scale(1.5, 1.5); // 先转后缩,和先缩后转结果完全不同 p.drawPixmap(-50, -50, pix); p.end(); // 必须调用,否则部分绘制不会提交顺序这件事值得单独说:translate、rotate、scale 三个变换是有叠加顺序的,代码里写的先后就是实际生效的先后。想让图片绕自己的中心旋转,必须先 translate 到中心再 rotate;如果先 rotate 再 translate,转的是坐标系,图片会绕着画布左上角甩出去。
缩放还有一个坑:QPainter::scale 放大后,绘制线条的宽度也会跟着放大,1 像素的边框会变成 1.5 像素,看起来发虚。要固定线宽,得用 QPen 的 setCosmetic(true),让画笔宽度不随变换缩放。
手册里“模糊效果”“阴影效果”用的是 QGraphicsBlurEffect 和 QGraphicsDropShadowEffect,这两个类只作用于 QWidget 和 QGraphicsItem,不能直接怼到 QImage 上。想对图片做模糊,常见做法是把 QImage 放进 QGraphicsScene 再渲染出来,或者自己写一个简单的盒式模糊。这个边界手册没明说,很多人照着写发现没效果,就是卡在这。
4.3 QSqlDatabase 与 SQLite:先查驱动再连库
第 10 章从查询驱动开始,这个顺序是对的,实际开发里也建议照这个顺序排查。
// 第一步:确认驱动列表里有没有 QSQLITE qDebug() << QSqlDatabase::drivers(); QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "main"); db.setDatabaseName("demo.db"); // 相对路径,落在工作目录 if (!db.open()) { qDebug() << "open failed:" << db.lastError().text(); return; } QSqlQuery q(db); q.exec("CREATE TABLE IF NOT EXISTS user(" "id INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT NOT NULL," "age INTEGER)"); q.prepare("INSERT INTO user(name, age) VALUES(?, ?)"); // 占位符,防注入也省转义 q.addBindValue("A同学"); q.addBindValue(20); q.exec();第一个参数是驱动名,第二个是连接名。连接名看着多余,但一个程序连两个库时它是唯一区分手段,不写的话第二次 addDatabase 会覆盖第一次,报“duplicate connection name”警告。
路径是另一个高频坑。setDatabaseName 给相对路径时,文件落在进程的当前工作目录,而不是 exe 所在目录。在 IDE 里跑没问题,双击 exe 就发现数据库“消失”了,其实是创建到了别处。稳妥做法是用 QCoreApplication::applicationDirPath() 拼绝对路径。
驱动列表为空,说明 .pro 里没写 QT += sql,或者发布时 sql 插件没跟着走。这两件事现象一样,原因完全不同,用 drivers() 的输出就能区分。
5. 避坑与排查:复现这套范例时最容易翻车的五处
这份手册的范例是能跑的,但把它们搬进自己的工程,失败率不低。下面五条是我在不同机器、不同项目里反复遇到的,按“现象、原因、解决”记下来,遇到了直接对号入座。
5.1 构建期的两个高频问题
第一条,现象是编译时报一堆 undefined reference,全是 QSqlDatabase、QTcpSocket、QMediaPlayer 这类符号,代码本身没有任何报错。原因是 .pro 里漏了模块声明,头文件能找到(因为 Qt 的头文件搜索路径是全局的),但链接期找不到对应的库。解决就一行:按手册章节补 QT += sql / network / multimedia,补完必须重新执行 qmake,光点构建不生效。
第二条,现象是改完图标、或者新加了一张图片,程序跑起来还是旧图标,甚至报“QPixmap: Cannot open file”。原因是 .qrc 的增量编译没触发,或者文件路径里的相对位置和 qrc 所在目录对不上。qrc 里的 file 路径是相对 .qrc 文件本身的,不是相对项目根目录,这一点手册范例里用的是 resource/xxx.ico,很容易被理解成相对工程。解决是先在构建菜单执行 qmake,再清理重编译;路径对不上就按 qrc 所在位置重新数一遍层级。
5.2 运行期的两个行为异常
第三条,无边框窗口拖不动,或者一拖就飞出屏幕。原因有三种可能:setWindowFlags 调在了 show() 之后;mouseMoveEvent 里先更新了 last 再算差值;窗口有父对象,x()/y() 是相对父窗口的值,move 的基准错了。解决是逐项核对,先看 flags 的调用位置,再检查 last 的更新顺序,最后看构造函数有没有传 parent。
第四条,给主窗口设了 setStyleSheet(“background:red”) 之后,里面的按钮、标签背景全变了样。原因是样式表会往子控件继承,QWidget 的 background 属性影响面尤其大。解决是把样式限定到具体类名,写成QMainWindow { background:red; },或者干脆只给一个占满窗口的 QLabel 设背景,主窗口本身不动。
5.3 发布期的一个经典故障
第五条,现象是开发机上跑得好好的,拷到另一台机器双击 exe,要么没有任何反应,要么弹出“could not find or load the Qt platform plugin windows”。原因是只拷了 exe,没带 Qt 的运行时 DLL 和平台插件目录。解决是用 Qt 自带的部署工具在同目录跑一次:
# 在 exe 所在目录执行,把所有依赖和插件收过来 windeployqt --release --no-translations app.exe执行完会多出 platforms、imageformats、sqldrivers 等目录。其中 platforms/qwindows.dll 缺了就是上面那个报错,sqldrivers/qsqlite.dll 缺了就是数据库连不上但没有任何提示。参数说明:--release 对应 release 构建,--no-translations 去掉语言包能省几 MB,需要多语言支持就别加。
再往后做成安装包,手册第 14 章给了两种思路,一个是自解压式的打包,一个是走安装包制作工具。我一般先用 windeployqt 把依赖收全,再整体塞进安装包,顺序反了会漏文件。
6. 把目录当索引:二次利用与验收方法
这份 PDF 我用了几年,最有价值的用法不是逐章读,是把它当一张验收表。做法很简单:每接手一个 Qt5 工程,先按手册的十四章过一遍,看这个项目用到了哪几块,然后在目录旁边标注“已复现 / 待验证 / 不涉及”。标注完,工程的边界就清楚了。
下面这张表是我常用的对照表,每一行对应手册的一章,右边是验收动作和通过判据。它比读十页正文都管用,因为每一步都能自己验证。
| 手册章节 | 验收动作 | 通过判据 |
|---|---|---|
| 第 2 章 窗体 | 改标题、换图标、锁尺寸 | 三处都生效,且样式没污染子控件 |
| 第 3 章 控件 | 用一个表单页放 8 个以上控件 | 拉伸窗口时布局不错位 |
| 第 5 章 文件 | 写一个 INI 再读回来 | 键值类型一致,缺失键走默认值 |
| 第 6 章 图形 | 图片旋转缩放后保存 | 边缘不糊,线宽不随缩放变化 |
| 第 10 章 数据库 | 建表、插一条、查出来 | drivers() 里有 QSQLITE |
| 第 14 章 打包 | 拷到另一台机器运行 | 无插件报错,图标正常 |
配合这张表,我还会跑一段小脚本扫自己的范例目录,确认每个章节的工程都还在、还能编。它不检查代码质量,只检查“有没有丢东西”,但这恰恰是最容易出问题的地方。
# 扫描范例根目录,按手册章节核对工程文件是否齐全 import os ROOT = "./QtSamples" REQUIRED = ["main.cpp", "mainwindow.cpp", "mainwindow.h"] # 每个范例的最小集 for chapter in sorted(os.listdir(ROOT)): path = os.path.join(ROOT, chapter) if not os.path.isdir(path): continue for name in sorted(os.listdir(path)): proj = os.path.join(path, name) if not os.path.isdir(proj): continue missing = [f for f in REQUIRED if not os.path.exists(os.path.join(proj, f))] # .pro 文件名不固定,按后缀匹配更稳 has_pro = any(f.endswith(".pro") for f in os.listdir(proj)) if missing or not has_pro: print(f"[缺件] {chapter}/{name} -> {missing or '.pro'}")脚本干的事很朴素:遍历章节目录,检查每个范例工程是否带着最小文件集和 .pro。参数上 REQUIRED 可以按自己的模板改,比如有的项目用 widget.cpp 而不是 mainwindow.cpp。跑一遍,缺件的直接打印出来,比一个个点开看快得多。
这套方法里最值钱的一条经验,是每复现一个范例,先原样跑通,再只改一个参数看结果怎么变。改尺寸就只改尺寸,改颜色就只改颜色,一次动一个变量。我曾经图省事,一次性把窗口尺寸、背景色、图标全换了,结果窗口显示异常,排查了快两个小时,最后发现是 setStyleSheet 把 QLabel 染红导致看起来像“窗口变小了”。从那以后我每次复现范例都强制走一遍“跑通—改一个参数—再跑通”的流程,不跳步。
希望这份拆解能帮你在拿到这份 PDF 时少走点弯路,先跑通环境,再按目录挑条目,剩下的就是耐心了。
本文还有配套的精品资源,点击获取