Files
张宗平 1053ebf41c 教案基准切换:新版 10 天安排 + 4 天版大纲(含中初级误区审计)
- docs/SCHEDULE_2026_10DAY.md:2026-07-05 新版 10 天安排正式文本,
  取代 TRAINING_PLAN_2026.md 旧执行计划作为课堂设计基准
- docs/teaching/OUTLINE_4DAY.md:4 天讲授 + Day5 综合案例的正式教案大纲,
  含逐天设计、旧主题去向决策(QSS→Day3、JSON→Day4、QThread→Day5 提升)、
  中初级学员误区盲点审计(10 张新坑卡:所有权/编码路径/连接语义三主线)、
  项目就绪度与高阶兴趣钩子评估
- OUTLINE.md 顶部加基准变更横幅,5 天版保留作历史参照与回退预案
- 顺带收编两篇此前未跟踪的自学文档 MODERN_CPP.md / MODERN_CPP_20PLUS.md
  (大纲与幻灯片多处链接它们)
2026-07-05 12:49:32 +08:00

493 lines
33 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 教案大纲 —「C++/Qt 方向」5 天实训(对齐官方方案书 Day1–5)【已被 4 天版取代】
> **⚠️ 基准变更(2026-07-05)**:收到新版 10 天实训安排
> [`docs/SCHEDULE_2026_10DAY.md`](../SCHEDULE_2026_10DAY.md)Day14 讲授 + Day5
> 综合案例 + Day6–10 项目开发),现行教案已改为
> [`OUTLINE_4DAY.md`](OUTLINE_4DAY.md),幻灯片 `slides/day15.html` 也已按 4 天版重排。
> 本文件对齐的是**旧方案书**的 Day1–5 结构,保留作历史参照与概念/坑卡素材库
> (4 天版复用了本文全部 24 张卡片),以及 PRETEST 摸底大面积不达标时的回退预案。
> 配套材料:`docs/teaching/slides/day1.html` … `day5.html`reveal.js 交互幻灯片,
> 已 vendor 进仓库,离线可用,见文末「材料与播放方式」)。
>
> 依据:`docs/TRAINING_PLAN_2026.md`《四川大学软件学院 2026 年夏 C/C++ 项目实习
> 实训方案书 V1.2》「五、执行计划 (二) C++方向」。方案书 C++方向共 10 天,
> **Day6–10 是纯项目实作(学员编码+教员指导+成果展示),没有课堂讲授内容**,
> 不在本教案范围内;本教案覆盖 Day1–5 的全部课堂教学。
## 0. 受众假设与本轮调整说明(显式声明,供随时纠偏)
**本教案假定学员已掌握 C++ 基础语法与面向对象概念**,方案书对 Day1 下午的官方
定位就是"C++ 高级程序开发**回顾和提升**"——复习强化,不是零基础教学。这条假设
不是拍脑袋定的,Day1 模块 B 开始前建议先用
[`docs/teaching/PRETEST.md`](PRETEST.md)(10 题、10 分钟摸底测验)实测一次,
按测验结果决定是照原节奏推进还是回退到语法讲解(该文件里有对应的分层预案)。
**与仓库里此前一版教案的关系**`docs/teaching/` 下此前有一版自定义 4 天教案
3 天 C++ 强化 + 1 天 Qt,讲:练 ≈ 1:3),是当时按用户选定的另一种日程假设做的。
**本版替换那一版**,严格对齐官方 10 天方案书 C++ 方向的 Day1–5 结构——差异很
大:官方结构里 C++ 只占 Day1 下午半天(其余 4.5 天都是 Qt),所以 C++ 内容从
原来的 3 天深度压缩为半天的**精选亮点 + 自学指针**,不再逐章深入;Qt 内容则
从原来压缩的 1 天摊开到 Day2–5 四天,覆盖面明显更广。
**内容缺口的显式处理**(这是本轮设计的关键决策,写清楚以便后续纠偏):
- 官方方案书 Day34 提到的 **Model/View、JSON 解析、QThread 多线程、
QPropertyAnimation 动画、QStateMachine 状态机**,仓库 `p03/`wiki 抓取内容)
原本完全没有对应代码。本轮已在 `docs/teaching/examples/` 补齐 **6 个最小可
运行示例**(含配套 README 教学文档),构建已接入根 `CMakeLists.txt`
`add_teaching_example` 分组,见该文件注释),**均已 `QT_QPA_PLATFORM=offscreen`
离屏验证跑通**。这些示例是手工维护的教学补充,**不属于** `tools/gen_part3.py`
的生成产物,重新生成 `p01`/`p03` 不会影响它们。
- 以下两项**仍按"纯概念 / 自学"处理,不写可运行代码**(原因见括号):
- **Vcpkg 包管理器**Day1)——需要安装真实 vcpkg 工具链,超出"最小示例"
范畴,且与仓库当前依赖面(host gcc/g++ + 隔离 Qt 5.14.2)无关;
- **OpenCV 人脸/目标检测**(Day5)——方案书原文本身就标注"可选择",属于
非必修拓展,引入 OpenCV 会新增一条完整的三方库工具链。
- 如果后续需要这两项也做成可运行示例,请明确提出——这会涉及新增外部依赖,
需要单独确认。
**核心概念覆盖原则**(本轮追加):此前一版的"讲"部分基本只是代码演示(贴代码 + 一句出处
说明),没有讲透知识点本身的原理。本轮改为:**每个"讲"知识点在大纲里至少固定两项——核心
概念(是什么/为什么,不只是怎么写)+ 常见的坑(注意事项/误区)**,随知识点内联在下面各天
的表格后。这是**覆盖清单**而非固定讲稿:课堂实际讲解深度按学员反馈现场调整(PRETEST 摸底
好、进度快,可以点到为止;反馈吃力,可以就某个概念多展开几分钟),但大纲必须列全这些点,
以免讲课时漏掉、或降级回退到纯代码演示。幻灯片 `day1.html``day5.html` 每个知识点前都配了
对应的"核心概念/常见的坑"卡片(`custom.css` 里的 `.concept-box`/`.pitfall-box`),大纲里的
条目和幻灯片内容一一对应,改一处要同步改另一处。
## 1. 总体目标
1. 学员能说出 C++ 相对 C 的关键扩展机制在**哪个标准版本**成立/失效(复习向,
不重新从语法教起)。
2. 学员能读懂并扩展一个 Qt 桌面小程序,覆盖信号槽、控件、布局/QSS、事件、
绘图、文件 IO/JSON、Model/View、多线程、动画/状态机**中的大多数**(覆盖面
是 Qt 教学的重点,深度服从进度)。
3. 每天结束都有一个**可运行、可验证**的产出。
4. 学员能看懂本次实训的项目管理流程(JIRA/Git/OASIS),为 Day610 的项目实作
打基础。
## 2. 课时结构
每天 6 课时(45 分钟/课时 ×6 = 270 分钟),分两个模块。**Day1 上午是开训/职业
素养内容,没有编码练习**,用讨论型任务卡代替;其余全部保持"讲:练"结构:
| 模块 | 时长 | 讲授 | 练习/讨论 | 小结/答疑 |
|---|---|---|---|---|
| 模块 A(上午,3 课时) | 135min | 35min | 95min | 5min |
| 模块 B(下午,3 课时) | 135min | 35min | 95min | 5min |
Day25 讲:练 ≈ 1:2.7;Day1 模块 A 是讨论型任务(不追求"练"的编码产出),
模块 BC++/CMake/Vcpkg)内容压缩,35min 讲授只做"巡礼式"精选亮点。
## 3. 五天总览
| Day | 官方主题 | 模块 A | 模块 B |
|---|---|---|---|
| 1 | 开训 + C++回顾/工具链 | 团队建设/Scrum/C++企业应用(讨论,无编码) | C++精选回顾 + CMake实操 + Vcpkg概念 |
| 2 | Qt开发环境/信号槽/基础控件 | 项目结构/信号槽 `p03/ch0204` | 控件/布局/QSS美化 `p03/ch08` + 新示例 `qt_qss_styling` → 计算器案例 |
| 3 | 高级控件/事件/文件IO/JSON | Model/View 新示例 + 事件拦截 `p03/ch09` | 文件IO `p03/ch11` + JSON 新示例 → 本地数据存储案例 |
| 4 | 2D绘图/多线程/动画/状态机 | 2D绘图 `p03/ch10` + QThread 新示例 | 动画+状态机新示例 → 交互式小游戏案例 |
| 5 | 团队协同/综合案例/OpenCV可选 | 团队协同开发(概念,用方案书图) | 综合案例复盘 + OpenCV/AI 自学指针 |
标注约定:**讲** = 课堂演示讲解,**练** = 学员必须动手完成,**参考** = 不在
课堂展开、留作课后自学。所有清单都是**精选**而非全集。
---
## Day 1 — 开训 + C++ 回顾/工具链
### 模块 A:团队建设 / 敏捷 Scrum / C++ 在企业中的应用(讨论型,无编码)
**讲(35min**
| 内容 | 素材 |
|---|---|
| 团队建设与文化、实训安排与考评要求 | 口头介绍,无固定素材 |
| 项目开发过程管理(JIRA/Git/OASIS/Wiki | `docs/TRAINING_PLAN_2026.md` 「七、质量保证」+ 图 `docs/images/training_service_flow.png``quality_four_platforms.png` |
| 敏捷 Scrum 项目开发管理 | 同上「服务流程图」里的迭代环节(需求分析→需求评审→项目研发→阶段评审→项目测试) |
| C++ 在企业中的应用 | 口头介绍,结合往届案例(如有) |
**核心概念与常见误区**
- **实习实训服务流程**:概念——需求分析→需求评审→项目研发/代码实现→阶段评审→项目测试/交付,
每环节都有明确"评审"节点把关;误区——把需求分析当走过场,跳过评审直接编码,后期返工成本
远高于前期对齐需求的成本。
- **四平台一标准**:概念——JIRA/Git/Wiki/OASIS 四个工具分别对应"谁在做什么/代码怎么变化/
知识怎么沉淀/进度怎么被看到"四个维度,缺一个都有协作盲区;误区——只用 Git 不用 Jira,代码
有版本历史但任务分工和进度没有可见记录。
**讨论(95min,分组,非编码)**
- **任务卡 1**
- 目标:结合服务流程图,说清楚本组打算怎么分工使用 Jira(任务跟踪)+ Git(代码版本管理)
- 验收标准:每组提交一句话分工说明(谁负责建 Jira 看板/谁负责 Git 仓库初始化),能在图上指出对应环节
- 基础知识 · 参考:`docs/images/training_service_flow.png``docs/TRAINING_PLAN_2026.md` 「七、质量保证 (一)(二)」
- **任务卡 2**
- 目标:选定实训期间打算做的项目方向(Linux C 方向 或 C++/Qt 方向的备选商业项目之一)
- 验收标准:每组报出项目名称 + 一句话说明选择理由
- 基础知识 · 参考:`docs/TRAINING_PLAN_2026.md` 「八、备选商业项目介绍」
### 模块 B:C++ 环境搭建 / 高级程序回顾 / CMake / Vcpkg(压缩讲解)
**讲(35min,巡礼式,只挑「学过但容易讲不透」的点,不逐章展开)**
| 知识点 | 文件 | 一句话说明 |
|---|---|---|
| namespace 作用域 | `p01/ch03/namespace_syntax.cpp` | 对照 `docs/ERRATA.md`"作用域"不是"文件顺序" |
| 函数重载决议 | `p01/ch03/function_overload_mechanism.cpp` | 名字改写与重载集合,二义性从哪来 |
| 引用的本质 | `p01/ch03/reference_essence.cpp` | "引用是自动解引用的 const 指针" |
| 虚析构与内存泄漏 | `p01/ch04/s08/virtual_dtor_purpose.cpp` | 基类指针 delete 派生类对象的经典坑 |
| 模板与 STL 速览 | `p01/ch05/class_template_basics.cpp``p01/ch09/s03/vector_iterator.cpp` | 泛型编程 + 容器/迭代器,只点名不展开 |
| C++11/14/17/20+ 新标准巡礼(概念,无演示) | 无仓库代码 | 让学员看到 C++ 不是停滞的语言:auto/智能指针/移动语义/lambda(11) → concepts/ranges/协程(20),每 3 年一次实打实的能力升级 |
| CMake 项目构建 | 本仓库 `CMakeLists.txt``p03/CMakeLists.txt` | 用真实项目当教材:`option(BUILD_QT_PART)``add_subdirectory`、每个示例一个 `add_executable` 目标 |
| Vcpkg 包管理器(概念,无演示) | 无仓库代码 | C++ 生态的包管理器,解决"第三方库怎么装、怎么被 CMake 找到"的问题;本仓库用的是"隔离 Qt 手动装"而非 vcpkg,两种思路都要认识 |
**核心概念与常见的坑**(每个知识点至少覆盖这两项,课堂讲解深度按学员反馈调整;对应幻灯片
`day1.html` 每个知识点前的"核心概念/常见的坑"卡片):
- **namespace 作用域**:概念——作用域可跨文件延续、跨块合并,不是"文件顺序";坑——头文件里
`using namespace std;` 会把命名空间污染带给所有 include 它的地方。
- **函数重载决议**:概念——编译器靠名字改写(name mangling)区分重载,二义性源于"同样好"的
候选而非代码写错;坑——隐式类型转换会扩大候选集,可能触发意料之外的重载版本。
- **引用的本质**:概念——"自动解引用的 const 指针"这一视角能解释必须初始化、不能重新绑定;
坑——返回局部变量的引用,函数结束后变成悬空引用。
- **虚析构与内存泄漏**:概念——基类指针 delete 派生类对象时,只有虚析构才会走虚函数表调用到
派生类析构;坑——只要类可能被继承+可能被基类指针删除,就该把析构声明为 virtual。
- **模板与 STL 速览**:概念——泛型编程让代码脱离具体类型复用,迭代器是容器与算法之间的统一
接口;坑——容器扩容/删除元素后旧迭代器可能失效,继续用是未定义行为。
- **C++11/14/17/20+ 新标准巡礼**:概念——C++11auto/智能指针/移动语义/lambda/语言级线程)
→ C++14(泛型 lambda/返回类型推导)→ C++17(结构化绑定/if-init/optional·variant·string_view/
filesystem)→ C++20concepts/ranges/协程/模块/三路比较),每个标准都是实打实的能力升级,
不是小修小补;坑——新标准不等于现在能用,生产环境编译器/CI 常年落后最新标准 1–2 个版本,
用新特性前先确认目标工具链 `-std=` 支持到哪(可用 `tools/std_probe.py` 自己验证)。
- **CMake 项目构建**:概念——CMake 是"生成构建系统的工具"而非构建系统本身,`-S -B` 生成、
`--build` 才是真正编译;坑——改了生成器相关配置(换生成器/清缓存)后要重新走一次 `-S -B`
- **Vcpkg 包管理器**:概念——一次 `vcpkg install` 配合 CMake toolchain 文件,`find_package`
即可自动定位头文件和库;坑——不是所有第三方库都必须用包管理器装,本仓库选择"隔离 Qt 手动装"
是精确控制版本/路径的另一种取舍,两种思路都要认识。
**练(95min**
- **任务卡 1 · 走一遍真实构建流程**
- 目标:独立完成本仓库 Part1 的配置+构建,不看着教师操作
- 验收标准:`cmake -S . -B build -G Ninja -DBUILD_QT_PART=OFF` 配置成功;`cmake --build build` 144 个目标全绿;能说出 `option(BUILD_QT_PART)` 的作用
- 基础知识 · 参考:仓库根 `CMakeLists.txt`<span class="ext-ref">CMake 官方文档 Tutorial(选读·需联网)</span>
- **任务卡 2 · 修一个真实的坑**
- 目标:从 `docs/ERRATA.md` 挑 2 条记录,在对应源文件里理解"wiki 原文错在哪、现在的写法为什么对"
- 验收标准:能用一句话向同组同学解释清楚,不能只念代码
- 基础知识 · 参考:`docs/ERRATA.md`
- **任务卡 3(选做)· 版本探针**
- 目标:用 `tools/std_probe.py` 验证一个标准版本差异点(如三目运算符可否作左值)
- 验收标准:跑出至少一个"该标准报错、另一个标准不报错"的对比结果,对照 `docs/VERSION_NOTES.md` §2
- 基础知识 · 参考:`tools/std_probe.py``docs/VERSION_NOTES.md`
**当日产出验证**`cmake --build build --target all` (Part1) 全绿;每组提交一句话
分工说明 + 至少 1 条 ERRATA 坑点讲解。
---
## Day 2 — Qt 开发环境 / 信号槽 / 基础控件 / 布局与 QSS
### 模块 A:项目结构 / 第一个程序 / 信号槽
**讲(35min**
| 知识点 | 文件 |
|---|---|
| 最小 Qt 程序结构(`QApplication` + `exec()` | `p03/ch02/minimal_qt_app.cpp` |
| 控件创建与显示 | `p03/ch03/button_creation.cpp` |
| 内置 / 自定义 / lambda 信号槽 | `p03/ch04/builtin_signal_slot.cpp``custom_signal_slot.cpp``lambda_signal_slot.cpp` |
**核心概念与常见的坑**(对应幻灯片 `day2.html` 每个知识点前的"核心概念/常见的坑"卡片):
- **最小 Qt 程序结构**:概念——`QApplication`是唯一的事件循环拥有者,`app.exec()`阻塞进入
事件循环分发系统事件;`Q_OBJECT`触发 moc 生成信号槽/属性系统所需的元对象代码;坑——一个
进程只能有一个 `QApplication`;忘记 `exec()` 窗口一闪而过;用了信号槽却忘记 `Q_OBJECT`
会在链接阶段报 `undefined reference` 且报错不提示"缺宏"。
- **控件创建与显示**:概念——QObject 对象树父子所有权模型,parent 销毁会自动 delete 所有
子对象;坑——忘记传 parent 造成真实内存泄漏;`move()` 手动定位的控件一旦套上布局会被覆盖。
- **信号槽(内置/自定义/lambda)**:概念——观察者模式的 Qt 实现,发送者/接收者彻底解耦;
新语法 `&Class::signal` 编译期类型检查,旧字符串语法 `SIGNAL()/SLOT()` 运行时检查;连接
方式分 Direct/Queued/Auto;坑——旧语法写错不报编译错误只是运行时静默连接失败;重载信号
不加 `QOverload` 消歧义会编译报错。
**练(95min**
- **任务卡 1 · 跑通最小程序**
- 目标:编译运行 `minimal_qt_app``button_creation`
- 验收标准:`QT_QPA_PLATFORM=offscreen tools/run_qt.sh minimal_qt_app``button_creation` 均退出码为 0,无崩溃
- 基础知识 · 参考:`p03/ch02/minimal_qt_app.cpp``p03/ch03/button_creation.cpp`
- **任务卡 2 · 自定义信号槽改文本**
- 目标:给一个按钮连接自定义信号槽,点击后改变一个 `QLabel` 的文本内容
- 验收标准:编译通过;离屏或有 X 环境下点击/模拟点击后 `QLabel` 文本确实改变;用 `connect` 语法而非直接函数调用
- 基础知识 · 参考:`p03/ch04/custom_signal_slot.cpp`(主参考)、`lambda_signal_slot.cpp`lambda 槽写法对比)
- **任务卡 3(选做)· 重载信号消歧义**
- 目标:仿 `builtin_signal_slot.cpp`,给一个类定义两个同名不同参的信号,用 `QOverload` 消歧义连接
- 验收标准:两个重载信号都能各自触发对应的槽
- 基础知识 · 参考:`p03/ch04/builtin_signal_slot.cpp`
### 模块 B:常用控件 / 布局管理 / QSS 界面美化 → 计算器案例
**讲(35min**
| 知识点 | 文件 |
|---|---|
| 图片/文本标签控件 | `p03/ch08/label_pixmap.cpp``label_text_html.cpp` |
| 自定义控件与布局(`QHBoxLayout` | `p03/ch08/custom_widget.cpp` |
| **QSS 界面美化(教学补充)** | `docs/teaching/examples/qss_styling/qt_qss_styling.cpp` |
**核心概念与常见的坑**
- **资源系统 .qrc**:概念——`:/...` 路径是 Qt 资源系统,资源在编译期打包进可执行文件,运行时
不依赖外部文件;坑——`.qrc` 路径与代码引用不一致会运行时静默失败(不报编译错误);忘记接入
CMake 的 `qt5_add_resources`/AUTORCC 资源系统不生效。
- **布局管理(QHBoxLayout)**:概念——布局管理器接管子控件位置/大小计算,随窗口缩放自动重排;
布局对象也遵循 QObject 父子所有权;坑——同一控件不能属于两个布局;布局设置后又用
`setGeometry`/`move` 手动改位置会被下次刷新覆盖。
- **QSS 界面美化**:概念——语法脱胎于 CSS(类型/ID 选择器、伪状态)+ 层叠继承规则,作用对象
是 QWidget 树而非 DOM;坑——ID 选择器必须先 `setObjectName``setStyleSheet` 会级联影响
所有子控件,范围失控是最容易踩的坑。
**练(95min,计算器案例——练习任务,非预建方案)**
- **任务卡 1 · 计算器雏形**
- 目标:组合 `button_creation.cpp`(多个数字/运算符按钮)+ `custom_signal_slot.cpp`(点击信号槽)+ `custom_widget.cpp``QGridLayout``QVBoxLayout` 排布按钮),做出一个能加减乘除的最小计算器
- 验收标准:至少支持两个数 + 一种运算符的计算并在 `QLabel`/`QLineEdit` 上正确显示结果;界面不重叠、按钮可点击
- 基础知识 · 参考:`p03/ch03/button_creation.cpp``p03/ch04/custom_signal_slot.cpp``p03/ch08/custom_widget.cpp`
- **任务卡 2 · 用 QSS 美化计算器**
- 目标:给任务卡 1 的计算器套一份 QSS 样式(数字键/运算键/等号键区分颜色)
- 验收标准:至少 2 类按钮有不同的 `background-color`;至少 1 个按钮有 `:hover``:pressed` 效果
- 基础知识 · 参考:`docs/teaching/examples/qss_styling/qt_qss_styling.cpp`、[README](examples/qss_styling/README.md)
**当日产出验证**:现场演示计算器(`QT_QPA_PLATFORM=offscreen tools/run_qt.sh <target>`
或有 X 环境直接运行),至少完成一次正确运算 + QSS 美化。
---
## Day 3 — 高级控件与 Model/View / 事件机制 / 文件 IO 与 JSON
### 模块 AModel/View 架构 / 事件机制
**讲(35min**
| 知识点 | 文件 |
|---|---|
| **Model/View 模型视图架构(教学补充)** | `docs/teaching/examples/model_view/qt_model_view_basics.cpp` |
| 事件循环机制(复用 Day2 概念) | `p03/ch02/minimal_qt_app.cpp``app.exec()` |
| 自定义事件处理 / 事件过滤器 | `p03/ch09/event_override_customwidget.cpp``event_filter_mainwindow.cpp` |
| 鼠标事件 | `p03/ch09/mouse_event.cpp` |
**核心概念与常见的坑**(对应幻灯片 `day3.html`):
- **Model/View 架构**:概念——数据(`QAbstractItemModel`)与展示(`QListView`/`QTableView`
彻底拆开,同一模型可喂给多个视图;`QModelIndex` + 角色(`Qt::DisplayRole`等)统一寻址;
坑——改模型数据不发 `dataChanged`/`beginInsertRows` 信号,视图不会自动刷新。
- **事件循环:事件 vs 信号**:概念——`app.exec()` 事件循环分发 `QEvent`(复用 Day2);事件是
框架推给控件的通知,信号槽是对象间主动通知,两者不同机制;坑——主线程做耗时操作会卡住整个
事件循环(呼应 Day4 QThread)。
- **自定义事件处理 / 事件过滤器**:概念——两条拦截路径:重写 `xxxEvent()`(只影响自己)或
`installEventFilter()`(统一拦截多个控件);事件有 `accept()`/`ignore()` 状态决定是否继续
传播;坑——重写事件函数忘记调用基类实现,会丢失控件默认行为。
**练(95min**
- **任务卡 1 · Model/View 换视图**
- 目标:把 `qt_model_view_basics.cpp` 里的 `QListView` 换成 `QTableView`,模型代码不改
- 验收标准:编译通过,界面正常展示同一份数据,证明"同一个模型可以喂给不同视图"
- 基础知识 · 参考:`docs/teaching/examples/model_view/qt_model_view_basics.cpp`、[README](examples/model_view/README.md)
- **任务卡 2 · 事件过滤器实操**
- 目标:仿 `event_filter_mainwindow.cpp`,给一个控件装事件过滤器,拦截鼠标按下事件并打印坐标
- 验收标准:点击控件后终端/调试输出能看到坐标信息
- 基础知识 · 参考:`p03/ch09/event_filter_mainwindow.cpp``mouse_event.cpp`
### 模块 B:文件 IO / JSON 解析 → 本地数据存储案例
**讲(35min**
| 知识点 | 文件 |
|---|---|
| 文本文件读写 | `p03/ch11/qfile_basic.cpp``qtextstream_io.cpp` |
| 结构化二进制读写 | `p03/ch11/qdatastream_io.cpp` |
| **JSON 解析与写入(教学补充)** | `docs/teaching/examples/json_io/qt_json_parse_write.cpp` |
**核心概念与常见的坑**
- **QIODevice 统一读写抽象**:概念——`QFile`/`QTextStream`/`QDataStream` 都建立在 `QIODevice`
统一接口上,之后遇到网络/串口等设备也是同一套接口;`QTextStream`面向文本行、`QDataStream`
面向结构化二进制;坑——忘记检查 `open()` 返回值会静默失败;混淆文本/二进制模式会破坏内容。
- **JSON 序列化/反序列化**:概念——序列化/反序列化是一对互逆操作,正确性标准是"写出去再读
回来数据一致"round-trip);`QJsonValue` 是能装多种类型的变体;坑——不检查
`QJsonParseError` 会让格式错误的输入静默变成空文档;类型不匹配 `toInt()`/`toString()`
不报错只返回默认值。
**练(95min,本地数据存储案例——练习任务,非预建方案)**
- **任务卡 1 · JSON 落盘**
- 目标:把 `qt_json_parse_write.cpp` 的 JSON 数据实际写入一个文件(结合 `qfile_basic.cpp``QFile` 写法),再读回来解析校验
- 验收标准:程序运行后磁盘上生成一个 `.json` 文件,内容可读;重新运行程序能正确解析出之前写入的数据
- 基础知识 · 参考:`docs/teaching/examples/json_io/qt_json_parse_write.cpp`、[README](examples/json_io/README.md)、`p03/ch11/qfile_basic.cpp`
- **任务卡 2 · 本地数据存储小工具**
- 目标:设计一个"学员信息"结构(姓名/成绩/标签),支持增加一条记录并追加保存到 JSON 文件,重启程序后能读出之前保存的全部记录
- 验收标准:至少能存 2 条以上记录并在重启后完整读回;数据字段不丢失
- 基础知识 · 参考:`docs/teaching/examples/json_io/``p03/ch11/qtextstream_io.cpp`
**当日产出验证**:现场演示本地数据存储小工具,验证"写入 → 重启 → 读回"完整闭环。
---
## Day 4 — 2D 图形绘制 / 多线程 / 动画框架与状态机
### 模块 A:2D 绘图 / 多线程
**讲(35min**
| 知识点 | 文件 |
|---|---|
| `QPainter` 基础绘图 | `p03/ch10/qpainter_basic.cpp` |
| 绘图设备(`QImage`/`QPixmap`/`QPicture` | `p03/ch10/paint_device_qimage.cpp``paint_device_pixmaps.cpp``qpicture_io.cpp` |
| **QThread 多线程(教学补充)** | `docs/teaching/examples/qthread_worker/qt_thread_worker.cpp` |
**核心概念与常见的坑**(对应幻灯片 `day4.html`):
- **QPainter 绘图**:概念——Qt 绘制是框架驱动而非"想画就画",所有绘制只能发生在
`paintEvent` 里,由 Qt 按需自动调用;主动触发重绘要用 `update()`(异步排队);坑——在
`paintEvent` 之外直接画东西是未定义行为;数据变了忘记 `update()`界面不会自动重画。
- **QThread 多线程**:概念——线程亲和性(每个 QObject 归属于创建/`moveToThread`到的线程,
GUI 控件必须归属主线程);官方推荐 worker-object 模式而非继承 QThread;跨线程信号槽自动
变 Queued,无需手动加锁;坑——在工作线程直接操作 GUI 控件是未定义行为/崩溃常见来源;
`moveToThread`要在`connect``start()`之前完成。
**练(95min**
- **任务卡 1 · 画一个简单图形界面**
- 目标:仿 `qpainter_basic.cpp`,在 `paintEvent` 里画一个组合图形(如棋盘格/仪表盘刻度)
- 验收标准:编译通过,离屏或 X 环境下能看到图形正确绘制(可截图验证)
- 基础知识 · 参考:`p03/ch10/qpainter_basic.cpp`
- **任务卡 2 · 感受多线程不卡界面**
- 目标:把 `qt_thread_worker.cpp` 里的耗时计算换成一个更长的循环或 `QThread::msleep`,观察主线程/事件循环没有被卡住
- 验收标准:能说清楚"如果把这段计算直接放主线程会发生什么"(界面无响应),并解释 `moveToThread` 起的作用
- 基础知识 · 参考:`docs/teaching/examples/qthread_worker/qt_thread_worker.cpp`、[README](examples/qthread_worker/README.md)
### 模块 B:动画框架 / 状态机 → 交互式小游戏案例
**讲(35min**
| 知识点 | 文件 |
|---|---|
| **QPropertyAnimation 动画(教学补充)** | `docs/teaching/examples/property_animation/qt_property_animation.cpp` |
| **QStateMachine 状态机(教学补充)** | `docs/teaching/examples/state_machine/qt_state_machine.cpp` |
**核心概念与常见的坑**
- **QPropertyAnimation 动画**:概念——动画建立在属性系统(`Q_PROPERTY`,同样靠 moc 生成)
上,本质是"用缓动曲线在一段时间内不断调用属性的 setter",任何 `Q_PROPERTY` 都能被动画驱动;
坑——属性必须是真正的 `Q_PROPERTY`,随手传错属性名不会报错只是什么也不发生;动画作用对象
在播放完成前被销毁会导致悬空访问。
- **QStateMachine 状态机**:概念——让"状态"成为一等对象(`QState`),替代一堆 bool 标志位加
if-else;状态转移(`addTransition`)本质是信号触发的转移,与信号槽自然衔接;坑——忘记
`setInitialState``start()`,状态机对象存在但从未真正运行;状态未覆盖所有触发条件会导致
"卡住"。
**练(95min,交互式小游戏案例——练习任务,选做,非预建方案)**
- **任务卡 1 · 动画 + 状态机组合**
- 目标:把 `qt_property_animation.cpp` 的动画和 `qt_state_machine.cpp` 的状态切换组合到同一个窗口——例如按钮在"红灯"状态时禁止移动,"绿灯"状态时点击才触发动画
- 验收标准:状态切换正确(红灯不触发动画,绿灯触发动画);动画播放完整不报错
- 基础知识 · 参考:`docs/teaching/examples/property_animation/``docs/teaching/examples/state_machine/`
- **任务卡 2(选做)· 简易反应力小游戏**
- 目标:结合任务卡 1 + 鼠标事件(Day3 `mouse_event.cpp`+ 多线程计时(Day4 `qt_thread_worker.cpp`),做一个"状态变化后限时点击"的最小交互游戏
- 验收标准:游戏至少有"开始→进行中→结束"三个可辨识状态,计时或计分逻辑不崩溃
- 基础知识 · 参考:上述三个教学补充示例 + `p03/ch09/mouse_event.cpp`
**当日产出验证**:分组演示动画+状态机组合小工具(任务卡 2 选做,量力而行)。
---
## Day 5 — 团队协同开发 / 综合案例复盘 / OpenCV(可选自学)
### 模块 A:团队协同开发(概念讲解)
**讲(35min**
| 内容 | 素材 |
|---|---|
| 项目开发过程管理全景(JIRA + Git + WIKI + 钉钉直播) | `docs/images/training_service_flow.png` |
| 质量保证体系(四平台一标准) | `docs/images/quality_four_platforms.png` |
| 标准化考核标准 | `docs/images/assessment_standard.png` |
| 代码团队协同开发实操建议 | `docs/TRAINING_PLAN_2026.md` 「七、质量保证 (二) Git」 |
**核心概念与常见误区**(对应幻灯片 `day5.html`):
- **Git 提交习惯**:概念——每完成一个任务都提交代码并写清任务描述,提交粒度对应 Jira 任务
粒度,commit 信息是可追溯记录;误区——攒几天代码一次性提交、commit 信息写"update",历史
记录失去可追溯性。
- **标准化考核标准**:概念——项目管理评估(40%)+项目技术评估(30%)+职业素养评估(30%),技术
只占三成;误区——只顾埋头写代码、忽略项目管理和职业素养维度,另外 70% 权重被拉低。
**讨论(95min**
- **任务卡 1 · 分工复盘**
- 目标:结合考核标准图(项目管理评估/项目技术评估/职业素养评估三大块),对照 Day1 任务卡 1 定下的分工,检查执行情况
- 验收标准:每组能说出至少 1 项做得好的、1 项需要改进的
- 基础知识 · 参考:`docs/images/assessment_standard.png`
### 模块 B:综合案例复盘 + OpenCV/AI(可选,自学指针)
**讲(35min**
| 内容 | 说明 |
|---|---|
| Day2–4 案例回顾 | 请 1–2 组现场展示计算器/本地存储/动画状态机小游戏 |
| OpenCV 与 AI 机器学习(可选,自学指针,无配套代码) | 方案书原文标注"可选择";如学员感兴趣,自学方向为 `cv::CascadeClassifier` 人脸检测入门,<span class="ext-ref">OpenCV 官方教程(需联网,选读)</span> |
**核心概念与常见误区**
- **案例回顾**:概念——代码走查(code review)是团队协作常规环节,讲清"为什么这么设计"比
"功能能跑"更能体现对 Day2–4 核心概念的理解;误区——只演示能跑不讲为什么,观摩组提不出有效
问题,复盘失去巩固概念的作用。
- **OpenCV 与 AI(可选)**:概念——OpenCV 是独立于 Qt 的计算机视觉库,Qt 管界面/交互、
OpenCV 管图像算法,两者通过 `cv::Mat``QImage` 转换配合。
**练(95min,综合演练)**
- **任务卡 1 · 三选一深化**
- 目标:从计算器 / 本地数据存储 / 动画状态机小游戏中选一个继续深化(加一个新功能)
- 验收标准:现场演示新增功能正常工作,编译无报错
- 基础知识 · 参考:回顾 Day2–4 对应的教学补充示例与 README
**当日产出验证**:每组现场演示深化后的案例,教师按考核标准图三大块给出反馈,
为 Day6–10 的正式项目实作分组定方向。
---
## 4. 验证与考核方式
- 每天结束前 10 分钟:学员提交当天任务卡的产出文件,用 `cmake --build build` 验证
Qt 相关目标需先 `-DBUILD_QT_PART=ON -DCMAKE_PREFIX_PATH=$PWD/.qt514/5.14.2/gcc_64`)。
- Day5 结束:三选一深化案例的现场演示,作为 Day1–5 阶段的考核点,同时对齐
方案书「标准化的考核标准」三大评估维度(项目管理/项目技术/职业素养)。
- 教师侧参考答案:仓库已有的 `p03/` 文件和 `docs/teaching/examples/` 里的教学
补充示例都是可运行的正确版本,可直接用作评分参照;`docs/ERRATA*.md` 记录了
每一处 wiki 原文修正,可用于讲解"错在哪、对在哪"。
## 5. 材料与播放方式
- 大纲:本文件 `docs/teaching/OUTLINE.md`
- 交互幻灯片:`docs/teaching/slides/day1.html``day5.html``index.html` 为入口页。
- 教学补充示例:`docs/teaching/examples/`,6 个最小可运行 Qt 程序 + README
构建方式见根 `CMakeLists.txt``docs/teaching/examples` 一节;运行方式与
`p03/` 里的示例一致:`QT_QPA_PLATFORM=offscreen tools/run_qt.sh <target>`
- 技术方案:reveal.js 5.2.1(仅 `dist/` 核心 + `plugin/highlight`)已 vendor 进
`docs/teaching/slides/reveal/`,**零 CDN 依赖**,断网教室也能播放。任务卡里的
`<span class="ext-ref">` 标注的官方文档/外部教程是**选读项**,需要联网才能
打开,不影响幻灯片本身离线播放。
- 播放方式(代码链接用相对路径指向仓库源文件,需要 HTTP 而非 `file://` 才能正常跳转):
```bash
cd /home/charles/workspaces/qt-course
python3 -m http.server 8000
# 浏览器打开 http://localhost:8000/docs/teaching/slides/index.html
```
- 幻灯片内所有「文件」链接均为相对本仓库路径的可点击链接,点开后由浏览器直接展示
源码(配合上面的 http.server);教师也可直接在编辑器里打开同一路径带学员精读。