# 教案大纲 —「C++/Qt 方向」5 天实训（对齐官方方案书 Day1–5）【已被 4 天版取代】

> **⚠️ 基准变更（2026-07-05）**：收到新版 10 天实训安排
> （[`docs/SCHEDULE_2026_10DAY.md`](../SCHEDULE_2026_10DAY.md)，Day1–4 讲授 + Day5
> 综合案例 + Day6–10 项目开发），现行教案已改为
> [`OUTLINE_4DAY.md`](OUTLINE_4DAY.md)，幻灯片 `slides/day1–5.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 四天，覆盖面明显更广。

**内容缺口的显式处理**（这是本轮设计的关键决策，写清楚以便后续纠偏）：

- 官方方案书 Day3–4 提到的 **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），为 Day6–10 的项目实作
   打基础。

## 2. 课时结构

每天 6 课时（45 分钟/课时 ×6 = 270 分钟），分两个模块。**Day1 上午是开训/职业
素养内容，没有编码练习**，用讨论型任务卡代替；其余全部保持"讲:练"结构：

| 模块 | 时长 | 讲授 | 练习/讨论 | 小结/答疑 |
|---|---|---|---|---|
| 模块 A（上午，3 课时） | 135min | 35min | 95min | 5min |
| 模块 B（下午，3 课时） | 135min | 35min | 95min | 5min |

Day2–5 讲:练 ≈ 1:2.7；Day1 模块 A 是讨论型任务（不追求"练"的编码产出），
模块 B（C++/CMake/Vcpkg）内容压缩，35min 讲授只做"巡礼式"精选亮点。

## 3. 五天总览

| Day | 官方主题 | 模块 A | 模块 B |
|---|---|---|---|
| 1 | 开训 + C++回顾/工具链 | 团队建设/Scrum/C++企业应用（讨论，无编码） | C++精选回顾 + CMake实操 + Vcpkg概念 |
| 2 | Qt开发环境/信号槽/基础控件 | 项目结构/信号槽 `p03/ch02–04` | 控件/布局/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++11（auto/智能指针/移动语义/lambda/语言级线程）
  → C++14（泛型 lambda/返回类型推导）→ C++17（结构化绑定/if-init/optional·variant·string_view/
  filesystem）→ C++20（concepts/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

### 模块 A：Model/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）；教师也可直接在编辑器里打开同一路径带学员精读。
