幻灯片按 4 天版重排:Day1 全 C++、Day3 启用 ch05/ch06、Day5 综合案例

- Day1:开训 + 全天 C++(PRETEST 摸底环节内联;★所有权/★编译 vs 链接错误新卡)
- Day2:Qt 入门 + 信号槽 → 计算器(★栈控件双重释放/★lambda 第五参数/
  ★重复 connect/★QString 中文编码,均链到坑点复现示例)
- Day3:QMainWindow/QDialog(p03/ch05、ch06 首次排课,☆两张新卡)+ 布局/控件/QSS
- Day4:事件/绘图/QTableWidget + 文件系统/JSON(★工作目录/★文件编码)
- Day5:综合案例全天(student_manager 走查 + QThread 提升 + 流程规范)
- index.html 同步 4+1 结构;相对链接脚本校验 0 失效
This commit is contained in:
张宗平
2026-07-05 12:49:44 +08:00
parent 1053ebf41c
commit 0f81edcc74
6 changed files with 880 additions and 1132 deletions
+161 -155
View File
@@ -2,7 +2,7 @@
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Day 5 · 团队协同开发/综合案例复盘/OpenCV可选</title>
<title>Day 5 · 综合项目案例讲解:学员信息管理器</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="reveal/reveal.css">
<link rel="stylesheet" href="reveal/theme.css">
@@ -15,198 +15,204 @@
<section>
<h1>Day 5</h1>
<h3>团队协同开发 / 综合案例复盘 / OpenCV(可选自学</h3>
<p class="module-badge">C++/Qt 方向实训 · 第 5 / 5 天 · 最后一天课堂内容</p>
<h3>综合项目案例讲解:学员信息管理器(全天</h3>
<p class="module-badge">C++/Qt 方向实训 · 综合案例日 · 明天起进入项目开发</p>
<ul>
<li>模块 A(3 课时):团队协同开发(概念讲解</li>
<li>模块 B(3 课时):综合案例复盘 + OpenCV/AI(可选,自学指针)</li>
<li>上午:需求拆解 → 界面骨架 → 数据层(Day1–4 全部知识点的收束</li>
<li>下午:提升——QThread 防卡界面;开发流程与规范;分组定项目方向</li>
</ul>
<p><small>今天结束后 Day6–10 是纯项目实作(学员编码 + 教员指导 + 成果展示),
不再有课堂讲授——今天的分组结论会直接带到项目实作阶段。</small></p>
<p><small>目标:通过一个完整小软件讲清 Qt 开发的<b>流程、规范及方法</b>——
项目 = 已学知识点的组装。参考实现:
<a class="filelink" target="_blank" href="../examples/student_manager/qt_student_manager.cpp">examples/student_manager/</a>
<a class="filelink" target="_blank" href="../examples/student_manager/README.md">README</a>)。
Day6–9 学员项目开发,Day10 下午答辩。</small></p>
</section>
<!-- ===================== 模块 A:讲 ===================== -->
<!-- ===================== 上午 1:需求与骨架 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 讲授 35min</p>
<h2>团队协同开发 <span class="tag tag-lecture"></span></h2>
<p class="module-badge">上午 1 · 需求拆解 → 界面骨架</p>
<h2>从需求到窗口 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>质量保证:四平台一标准(复习) <span class="tag tag-lecture"></span></h3>
<img src="../../images/quality_four_platforms.png" style="max-height:520px;">
<p><small>出处:<a class="filelink" target="_blank"
href="../../TRAINING_PLAN_2026.md">docs/TRAINING_PLAN_2026.md</a> 「七、质量保证 (二) Git」</small></p>
<h3>需求拆解:先想清楚再动手 <span class="tag tag-lecture"></span></h3>
<div class="concept-box">
<h4>核心概念——开发流程的第一步不是写代码</h4>
<ul>
<li>需求:管理学员记录(姓名/成绩/标签),支持添加、保存、读取、批量导入。</li>
<li>拆功能 → 映射到已学组件:列表展示=QTableWidget(Day4)、添加=模态对话框(Day3)、
保存读取=QFile+JSON(Day4)、批量导入=后台线程(今天新授)、外壳=QMainWindow(Day3)。</li>
<li>拆完就得到任务清单——这正是 Day6 起你们在 Jira 上要做的事。</li>
</ul>
</div>
</section>
<section>
<h3>Git 提交习惯:核心概念 <span class="tag tag-lecture"></span></h3>
<h3>界面骨架走查 <span class="tag tag-lecture"></span></h3>
<div class="concept-box">
<h4>回指 Day34</h4>
<ul>
<li>QMainWindow:菜单(文件/记录/帮助)+ 工具栏 + 状态栏 + <code>setCentralWidget(table)</code></li>
<li>QAction 一个动作三处复用(菜单/工具栏/快捷键),<b>每个 action 都要 connect</b></li>
<li>添加记录:模态 <code>exec()</code> + <code>QFormLayout</code> + <code>QDialogButtonBox</code></li>
<li>表格 item 所有权:new 出来交给表格——父子所有权模型第 N 次出现。</li>
</ul>
</div>
<p><small>现场跑:<code>tools/run_qt.sh qt_student_manager</code>(离屏自测版:
<code>QT_QPA_PLATFORM=offscreen</code>,自动走完整验证流程)</small></p>
</section>
</section>
<!-- ===================== 上午 2:数据层 ===================== -->
<section>
<section>
<p class="module-badge">上午 2 · 数据层</p>
<h2>记录 ↔ JSON ↔ 磁盘 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>数据层走查 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
struct Student { QString name; double score; QString tag; };
QJsonObject toJson(const Student&); // 互逆的一对
Student fromJson(const QJsonObject&); // round-trip = 正确性标准
</code></pre>
<div class="concept-box">
<h4>回指 Day4(昨天作业的完整版)</h4>
<ul>
<li>数据结构与界面分离:<code>Student</code> 结构体不知道 QTableWidget 的存在——
这是 Model/View 思想的朴素版;</li>
<li>健壮性三处检查全部在场:<code>open()</code> 返回值、<code>QJsonParseError</code>
路径用 <code>applicationDirPath()</code> 拼。</li>
</ul>
</div>
<div class="pitfall-box">
<h4>复盘昨天作业的常见问题</h4>
<ul>
<li>保存和读取的字段名拼写不一致("score" vs "Score")——round-trip 一测就露馅,
这就是为什么验收标准是"写进去读出来一致"而不是"能生成文件"。</li>
</ul>
</div>
</section>
</section>
<!-- ===================== 下午 1QThread 提升 ===================== -->
<section>
<section>
<p class="module-badge">下午 1 · 提升:多线程防卡界面(新授)</p>
<h2>QThread:耗时操作不冻结界面 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>先看病症:把耗时活儿放主线程 <span class="tag tag-lecture"></span></h3>
<div class="pitfall-box">
<h4>现场演示</h4>
<ul>
<li><code>msleep</code> 循环直接放按钮槽里 → 整个窗口冻结:点不动、拖不动、
系统提示"未响应"——Day4 讲过:<b>主线程阻塞 = 事件循环停摆</b>
这是学员项目里最高频的"卡死"来源。</li>
</ul>
</div>
</section>
<section>
<h3>正解:worker-object 模式 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
auto* thread = new QThread(this);
auto* worker = new ImportWorker; // 不给 parent:有 parent 禁止 moveToThread
worker->moveToThread(thread); // 必须在 start() 之前
connect(thread, &QThread::started, worker, [worker]{ worker->doImport(10); });
connect(worker, &ImportWorker::rowReady, this, /* 更新表格 */); // 自动 Queued
thread->start();
</code></pre>
<div class="concept-box">
<h4>核心概念(三件套)</h4>
<ul>
<li><b>线程亲和性</b>:每个 QObject 归属一个线程,GUI 控件必须归主线程;</li>
<li><b>worker 对象</b>:官方推荐做法——耗时逻辑放 worker,<code>moveToThread</code>
搬进工作线程,而不是继承 QThread 重写 run</li>
<li><b>跨线程信号槽自动 Queued</b>(Day2 的连接方式今天闭环):worker 发信号、
主线程槽更新表格,不需要手动加锁。</li>
</ul>
</div>
<div class="pitfall-box">
<h4>常见的坑</h4>
<ul>
<li>在工作线程直接操作 GUI 控件——未定义行为,跨线程崩溃的头号来源;</li>
<li><code>moveToThread</code> 写在 <code>start()</code> 之后 / worker 带了 parent——都不生效。</li>
</ul>
</div>
<p><small>最小版:<a class="filelink" target="_blank"
href="../examples/qthread_worker/qt_thread_worker.cpp">examples/qthread_worker/</a> ·
案例集成版:<a class="filelink" target="_blank"
href="../examples/student_manager/qt_student_manager.cpp">student_manager 的 startImport()</a></small></p>
</section>
</section>
<!-- ===================== 下午 2:流程与规范 ===================== -->
<section>
<section>
<p class="module-badge">下午 2 · 开发流程与规范 + 分组定方向</p>
<h2>进项目前的最后一课 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>Git 提交习惯 <span class="tag tag-lecture"></span></h3>
<div class="concept-box">
<h4>核心概念</h4>
<ul>
<li>接下来 Day6–10 项目实作,每人每完成一个任务都要提交代码到 Git 并写清任务描述——
提交粒度对应 Jira 任务粒度,commit 信息就是"这次提交做了什么、为什么"的可追溯记录</li>
<li>每完成一个任务就提交,提交粒度对应任务粒度;commit 信息说清"做了什么、为什么"——
它是项目的可追溯记录,评审和回滚都靠它</li>
</ul>
</div>
<div class="pitfall-box">
<h4>常见误区</h4>
<ul>
<li>几天代码一次性提交、commit 信息写"update":历史记录失去可追溯性,出问题无法
定位是哪次改动引入的。</li>
<li>攒几天代码一次性提交、信息写"update"——历史失去可追溯性,出问题无法定位是哪次
改动引入的。从 Day6 的第一次 commit 开始就按规范来</li>
</ul>
</div>
</section>
<section>
<h3>标准化考核标准 <span class="tag tag-lecture"></span></h3>
<img src="../../images/assessment_standard.png" style="max-height:520px;">
<p><small>出处:<a class="filelink" target="_blank"
href="../../TRAINING_PLAN_2026.md">docs/TRAINING_PLAN_2026.md</a> 「七、(五) 标准化的考核标准」</small></p>
</section>
<section>
<h3>考核标准:核心概念 <span class="tag tag-lecture"></span></h3>
<h3>考核标准与分组定方向 <span class="tag tag-lecture"></span></h3>
<div class="concept-box">
<h4>核心概念</h4>
<ul>
<li>项目管理评估(40%) + 项目技术评估(30%) + 职业素养评估(30%)——技术实现只占三成,
Day6–10 项目实作最终按这三个维度综合打分,不是"代码能跑就满分"</li>
<li>考核不只看代码:项目管理(任务拆解/进度可见)+ 项目技术 + 职业素养(协作/答辩表达)
多维度评估——只埋头写代码会丢掉其他维度的分</li>
<li>今天散场前每组确认:项目选题 + 初步分工 + 任务拆解草稿(上午"需求拆解"那页
就是模板)。</li>
</ul>
</div>
<div class="pitfall-box">
<h4>常见误区</h4>
<ul>
<li>只顾埋头写代码、忽略项目管理和职业素养维度(如任务跟踪是否及时更新、沟通是否
到位):技术分再高,另外 70% 的权重会明显拉低总评。</li>
</ul>
</div>
</section>
</section>
<!-- ===================== 模块 A:讨论 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 讨论 95min</p>
<h2>动手:分工复盘 <span class="tag tag-discuss"></span></h2>
</section>
<section>
<div class="task-card discuss-card">
<h4>任务卡 1 · 分工复盘 <span class="tag tag-discuss"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd>结合考核标准图(项目管理评估/项目技术评估/职业素养评估三大块),对照 Day1
任务卡 1 定下的分工,检查执行情况</dd>
<dt>验收标准</dt>
<dd>每组能说出至少 1 项做得好的、1 项需要改进的</dd>
<dt>基础知识 · 参考</dt>
<dd>上一节"标准化考核标准"图 · Day1 任务卡 1 的分工结论</dd>
</dl>
</div>
</section>
</section>
<!-- ===================== 模块 B:讲 ===================== -->
<section>
<section>
<p class="module-badge">模块 B · 讲授 35min</p>
<h2>综合案例复盘 + OpenCV/AI(可选) <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>Day24 案例回顾 <span class="tag tag-lecture"></span></h3>
<p>请 1–2 组现场展示计算器 / 本地数据存储 / 动画状态机小游戏,其他组观摩提问。</p>
<div class="concept-box">
<h4>核心概念</h4>
<ul>
<li>代码走查(code review)是团队协作的常规环节——展示时讲清"为什么这么设计"比"功能能跑"
更能体现对 Day2–4 核心概念(对象树/信号槽/Model-View/线程亲和性……)的理解程度。</li>
</ul>
</div>
<div class="pitfall-box">
<h4>常见误区</h4>
<ul>
<li>只演示"能跑"不讲"为什么":观摩组提不出有效问题,复盘就失去了巩固概念的作用。</li>
</ul>
</div>
</section>
<section>
<h3>OpenCV 与 AI 机器学习(可选,自学指针) <span class="tag tag-lecture"></span></h3>
<div class="concept-box">
<h4>核心概念</h4>
<ul>
<li>OpenCV 是独立于 Qt 的计算机视觉库,Qt 负责界面/交互,OpenCV 负责图像算法——两者通过
"内存里的图像数据格式转换"(<code>cv::Mat</code><code>QImage</code>)配合,是很多
桌面视觉类项目的典型组合方式。</li>
</ul>
</div>
<div class="verify-box">
<h4>缺口说明</h4>
<p>方案书原文对本项标注"可选择",属于非必修拓展。引入 OpenCV 会新增一条完整的
三方库工具链,本仓库暂不提供可运行示例。如果感兴趣,自学方向是
<code>cv::CascadeClassifier</code> 人脸检测入门。</p>
</div>
<p><small><span class="ext-ref">OpenCV 官方教程(doc.opencv.org,选读·需联网)</span></small></p>
</section>
</section>
<!-- ===================== 模块 B:练(综合演练) ===================== -->
<section>
<section>
<p class="module-badge">模块 B · 练习 95min(综合演练)</p>
<h2>动手:三选一深化 <span class="tag tag-practice"></span></h2>
</section>
<section>
<div class="task-card">
<h4>任务卡 1 · 三选一深化 <span class="tag tag-practice"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd>从计算器 / 本地数据存储 / 动画状态机小游戏中选一个继续深化(加一个新功能)</dd>
<dt>验收标准</dt>
<dd>现场演示新增功能正常工作,编译无报错</dd>
<dt>基础知识 · 参考</dt>
<dd>回顾 Day2–4 对应的教学补充示例与 README:
<a class="filelink" target="_blank" href="../examples/qss_styling/README.md">qss_styling</a> ·
<a class="filelink" target="_blank" href="../examples/json_io/README.md">json_io</a> ·
<a class="filelink" target="_blank" href="../examples/property_animation/README.md">property_animation</a> ·
<a class="filelink" target="_blank" href="../examples/state_machine/README.md">state_machine</a></dd>
</dl>
<div class="verify-box" style="font-size:0.55em;">
<h4>兴趣钩子(各 5 分钟演示,选学)</h4>
<p>给界面加动效:<a class="filelink" target="_blank"
href="../examples/property_animation/qt_property_animation.cpp">QPropertyAnimation</a>(任何
Q_PROPERTY 都能被缓动曲线驱动) ·
多状态 UI<a class="filelink" target="_blank"
href="../examples/state_machine/qt_state_machine.cpp">QStateMachine</a>(状态成为一等对象,
替代一堆 bool 标志)· 线程的更高抽象:QtConcurrent/线程池。</p>
</div>
</section>
</section>
<!-- ===================== 收尾 ===================== -->
<section>
<h2>当日产出验证 · Day15 阶段总验收</h2>
<h2>当日产出验证 · 进入项目期</h2>
<div class="verify-box">
<h4>现场演示</h4>
<p>每组现场演示深化后的案例,教师按考核标准图三大块给出反馈,为 Day6–10
的正式项目实作分组定方向。</p>
<h4>验收</h4>
<pre><code class="bash" data-trim>QT_QPA_PLATFORM=offscreen tools/run_qt.sh qt_student_manager
# 自测 1/2:JSON 写入→清空→读回,2 条记录完整
# 自测 2/2:后台线程导入 3 条,总计 5 条,界面未阻塞</code></pre>
<p>每组提交:项目选题 + 分工说明 + 任务拆解草稿。明天 Day6 起:学员编码 + 工程师指导,
Day10 下午项目答辩。</p>
</div>
<p class="footer-note"><small>Day1–5 课堂教学到此结束,Day6–10 进入项目实作——大纲
<a class="filelink" href="../OUTLINE.md">docs/teaching/OUTLINE.md</a></small></p>
<p><a class="filelink" href="index.html">返回目录</a> ·
<a class="filelink" href="day4.html">← 上一天</a></p>
</section>
<!-- ===================== 延伸阅读(全教案最末页) ===================== -->
<section>
<h2>延伸阅读 · Day6–10 项目实作支撑知识体系</h2>
<div class="verify-box">
<h4>16 个可选项目,除 C++/Qt 之外还需要哪些知识?</h4>
<p>系统梳理了实现这 16 个项目所需的 12 个辅助专业领域——数据结构与算法、
2D 游戏与碰撞、计算机图形学、数字图像处理、计算机视觉与机器学习(OpenCV/Haar/LBPH)、
编译原理与状态机、编码理论、计算机网络、并发编程、文件系统与增量同步、软件设计模式等,
<strong>每个核心知识点附 2–3 个已验证可用的网络课程链接</strong>(中英文兼顾)。</p>
<p>全文(serve.py 下本地一点即开):<a class="filelink" target="_blank"
href="../SUPPORTING_KNOWLEDGE.md">docs/teaching/SUPPORTING_KNOWLEDGE.md
&nbsp;&nbsp; qt-course-projs/docs/supporting-knowledge.md</a></p>
</div>
<p class="footer-note"><small>文档由项目仓库 <code>qt-course-projs</code> 维护,
本仓库以相对符号链接引用,单一源头、不复制;如直接用文件协议打开本页,请改用
<code>python3 docs/teaching/serve.py</code> 起本地服务以渲染该 Markdown。</small></p>
<p><a class="filelink" href="index.html">返回目录</a> ·
<a class="filelink" href="day4.html">← 上一天</a></p>
<p class="footer-note"><small>案例参考实现与讲解建议
<a class="filelink" target="_blank" href="../examples/student_manager/README.md">examples/student_manager/README.md</a> ·
大纲:<a class="filelink" href="../OUTLINE_4DAY.md">docs/teaching/OUTLINE_4DAY.md</a> §3</small></p>
<p><a class="filelink" href="index.html">返回目录</a> · <a class="filelink" href="day4.html">← 上一天</a></p>
</section>
</div>