Files
scuc-qt-course/tools/teaching_ppt/day5.py
T
张宗平 ea6f12e7ac PPT 版本幻灯片:生成器 + 5 天讲稿备注(已去 AI 味)
- tools/teaching_ppt/gen.py:python-pptx 生成引擎(页面内容与讲稿数据分离,
  幂等可重生成);tools/teaching_ppt/day1..5.py:每天的页面+讲稿数据
- docs/teaching/ppt/day1..5.pptx:5 份 PPT 产物,页面内容对应 slides/*.html,
  详细讲稿(建议时长/讲法/演示/互动/过渡)只写入演讲者备注,不进主页面
- 讲稿去 AI 味:破折号过度堆砌 152 处砍到 6 处(7.8‰ → 0.3‰)、
  升华腔(分水岭/点燃探索欲)清零、重复词(现场 26→11)替换;
  保留教学口语生动感(你们/当堂/点破/埋点/回指)与【过渡】悬念钩子
- 生成方式:python-pptx 在隔离 venv 装,不影响系统 Python;
  改 day*.py 的 notes 字段后重跑 gen.py 即可幂等重生成
2026-07-05 16:08:13 +08:00

172 lines
15 KiB
Python
Raw 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.
# -*- coding: utf-8 -*-
"""Day5 · 综合项目案例讲解:学员信息管理器(全天)—— 页面对应 slides/day5.html。"""
SLIDES = [
{
'kind': 'title',
'badge': 'C++/Qt 方向实训 · 综合案例日 · 明天起进入项目开发',
'title': 'Day 5 · 综合项目案例讲解:学员信息管理器',
'subtitle': '需求拆解 → 界面骨架 → 数据层 → 提升:QThread 防卡界面 → 流程规范',
'bullets': [
'上午:需求拆解 → 界面骨架(QMainWindow + QTableWidget + 对话框)→ 数据层(Student ↔ JSON',
'下午 1:提升——QThread 防卡界面(worker-object 模式)',
'下午 2:开发流程与规范(Git/考核)+ 分组定项目方向',
'Day6–9 学员项目开发;Day10 下午项目答辩',
],
'source': '参考实现:examples/student_manager/qt_student_manager.cppREADME',
'minutes': 5, 'segment': '开场',
'notes': """【定位】今天不是新知识日,是"组装日",把四天学的全部东西捏成一个完整小软件,再补最后一块拼图(多线程)。开场明确这个定位,让学员从"听课模式"切换到"项目模式"
【讲法建议】全天以"先看成品→拆解→回指知识点"的节奏推进,每个环节都让学员意识到"这部分我已经学过",建立项目信心。教师参考实现 examples/student_manager/ 全程作为对照。
【时长骨架】上午 1(需求加骨架)约 90 分钟;上午 2(数据层)约 90 分钟;下午 1(QThread 新授)约 70 分钟;下午 2(流程规范加分组)约 50 分钟。每段留 15 分钟缓冲。
【红线提醒】下午 QThread 是全天最高优先级保底项。上午超时宁可砍需求拆解的深度也不能砍 QThread,否则项目期"界面冻结/跨线程崩溃"答疑成本数倍返还。"""
},
{
'badge': '上午 1 · 需求拆解 → 界面骨架',
'title': '需求拆解:先想清楚再动手',
'boxes': [
('concept', '核心概念——开发流程的第一步不是写代码', [
'需求:管理学员记录(姓名/成绩/标签),支持添加、保存、读取、批量导入',
'拆功能 → 映射到已学组件:列表=QTableWidget(Day4)、添加=模态对话框(Day3)、保存读取=QFile+JSON(Day4)、批量导入=后台线程(今天新授)、外壳=QMainWindow(Day3)',
'拆完就得到任务清单——这正是 Day6 起你们在 Jira 上要做的事',
]),
],
'minutes': 25, 'segment': '上午 · 需求拆解',
'notes': """【地位】这一页是"从学生到工程师"思维转换的关键页:写代码之前先拆任务。中初级学员最常见的项目障碍不是"不会写"而是"不知道先写什么",本页就是治这个。
【讲法】白板当堂拆:把"学员信息管理器"这个一句话需求,逐条拆成 5 个功能点,再把每个功能点映射到已学组件。每映射一条就问"这是哪天学的?"让全班齐声回答 Day2/3/4。这个回指动作能极大强化"我已经具备做项目的全部知识"的信心。
【交付物】拆解的产物就是任务清单。明确告诉学员:"Day6 起你们在 Jira 上建的每一个 task,就应该长这样:一个动词开头、可验收、映射到具体技术点。"今天先有这个意识,明天分组就用得上。
【过渡】"任务清单有了,开始拼第一块:界面骨架。" """
},
{
'badge': '上午 1 · 需求拆解 → 界面骨架',
'title': '界面骨架走查',
'boxes': [
('concept', '回指 Day34', [
'QMainWindow:菜单(文件/记录/帮助)+ 工具栏 + 状态栏 + setCentralWidget(table)',
'QAction 一个动作三处复用(菜单/工具栏/快捷键),每个 action 都要 connect',
'添加记录:模态 exec() + QFormLayout + QDialogButtonBox',
'表格 item 所有权:new 出来交给表格——父子所有权模型第 N 次出现',
]),
],
'source': '现场跑:tools/run_qt.sh qt_student_manager(离屏自测:QT_QPA_PLATFORM=offscreen',
'minutes': 40, 'segment': '上午 · 骨架走查',
'notes': """【讲法】自顶向下:先跑参考实现 examples/student_manager 给全班看成品长什么样,然后关掉投影,带学员从空 main() 开始一步步搭——QApplication → MainWindow(继承 QMainWindow) → setCentralWidget(table) → 菜单/工具栏/action → connect。每一步都问"这步对应哪天的哪个示例"
【回指密度】本页 4 条 bullet 每条都是一次回指:QMainWindow→Day3、QAction→Day3(坑卡"忘 connect")、模态对话框→Day3、表格 item 所有权→Day4 加贯穿全课程的所有权主线。回指的密度本身就是"项目=已学内容组装"的最佳论证。
【代码组织建议】当场把 MainWindow 拆成 .h/.cpp,演示"声明在头、实现在 cpp"的工程结构,这是 Day6 起项目实作的基本功,今天第一次正式做。
【互动】故意演示一次"建了 action 忘 connect",菜单项灰色点了没反应,让学员用 Day3 坑卡的语言定位问题。坑卡回炉一次。
【过渡】"骨架立住了,往里灌数据。" """
},
{
'badge': '上午 2 · 数据层',
'title': '记录 ↔ JSON ↔ 磁盘',
'code': 'struct Student { QString name; double score; QString tag; };\nQJsonObject toJson(const Student&); // 互逆的一对\nStudent fromJson(const QJsonObject&); // round-trip = 正确性标准',
'boxes': [
('concept', '回指 Day4(昨晚作业的完整版)', [
'数据结构与界面分离:Student 结构体不知道 QTableWidget 的存在——Model/View 思想的朴素版',
'健壮性三处检查全部在场:open() 返回值、QJsonParseError、路径用 applicationDirPath() 拼',
]),
('pitfall', '复盘昨晚作业的常见问题', [
'保存和读取的字段名拼写不一致("score" vs "Score")——round-trip 一测就露馅',
'这就是为什么验收标准是「写进去读出来一致」而不是「能生成文件」',
]),
],
'minutes': 45, 'segment': '上午 · 数据层',
'notes': """【讲法】把昨晚作业的数据层"升级"成案例版本:结构体 Student 加两个自由函数 toJson/fromJson。重点讲"数据结构与界面分离"Student 不知道 QTableWidget 的存在,这是 Model/View 思想的朴素实践(回指昨天表格页的钩子)。
【当堂对照】让 1–2 位学员展示昨晚作业的数据层代码,对照参考实现讲差异,尤其是字段名拼写不一致的坑(昨晚作业的高发问题),round-trip 测试一跑就露馅,这正是"验收标准为什么是 round-trip"的活教材。
【三处健壮性检查】打开参考实现源码,逐处指认 open() 判返回值、ParseError 检查、applicationDirPath() 拼路径,四天坑卡的成果在此集中体现。让学员看到"这些检查不是教条,是真实代码里必须有的"
【代码组织】toJson/fromJson 放在独立的 student.h(纯数据加序列化),不依赖 Qt Widgets,演示"按依赖方向分层"的工程习惯。
【过渡】"功能齐了:能加、能存、能读。但有一个场景会让它冻住,下午第一项,新授。" """
},
{
'badge': '下午 1 · 提升:多线程防卡界面(新授)',
'title': '先看病症:把耗时活儿放主线程',
'boxes': [
('pitfall', '现场演示', [
'把 msleep 循环直接放按钮槽里 → 整个窗口冻结:点不动、拖不动、系统提示「未响应」',
'Day4 讲过:主线程阻塞 = 事件循环停摆——学员项目最高频的「卡死」来源',
]),
],
'minutes': 15, 'segment': '下午 · QThread 新授',
'notes': """【演示流程】在参考实现的"后台导入"按钮槽里,把 moveToThread 那套注释掉,直接在工作函数里 sleep 循环,跑给全班看:点导入后窗口完全冻结 5 秒,鼠标点哪都没反应,系统标题栏提示"未响应"
【讲法】让学员先用 Day4 的语言描述现象:"事件循环停摆了"。然后定性:"这是你们项目里最容易出现的卡死,根源不是代码错,而是把耗时活儿放错了线程。"
【不急着给解法】演示完病症后停一下,问"你们觉得该怎么解决?"收集学员想法(多数会猜"开个线程"),自然引出下一页的 worker 模式。先有痛点再给方案,教学效果远好于直接讲语法。
【过渡】"对,要开线程。但 Qt 开线程有个最容易被带歪的姿势,下一页讲正确做法。" """
},
{
'badge': '下午 1 · 提升:多线程防卡界面(新授)',
'title': '正解:worker-object 模式',
'code': 'auto* thread = new QThread(this);\nauto* worker = new ImportWorker; // 不给 parent:有 parent 禁止 moveToThread\nworker->moveToThread(thread); // 必须在 start() 之前\nconnect(thread, &QThread::started, worker, [worker]{ worker->doImport(10); });\nconnect(worker, &ImportWorker::rowReady, this, /* 更新表格 */); // 自动 Queued\nthread->start();',
'boxes': [
('concept', '核心概念(三件套)', [
'线程亲和性:每个 QObject 归属一个线程,GUI 控件必须归主线程',
'worker 对象:官方推荐——耗时逻辑放 worker,moveToThread 搬进工作线程,而非继承 QThread 重写 run',
'跨线程信号槽自动 Queued(Day2 连接方式闭环):worker 发信号、主线程槽更新表格,不需手动加锁',
]),
('pitfall', None, [
'在工作线程直接操作 GUI 控件——未定义行为,跨线程崩溃头号来源',
'moveToThread 写在 start() 之后 / worker 带了 parent——都不生效',
]),
],
'source': '最小版:examples/qthread_worker/ · 案例集成版:student_manager 的 startImport()',
'minutes': 40, 'segment': '下午 · QThread 新授',
'notes': """【地位】这是全天最高优先级的新授点,也是四天来埋的"主线程阻塞"暗线的收口,必须讲透,时间是 40 分钟,不能压缩。
【讲法顺序】先讲"不要怎么做":继承 QThread 重写 run 是网上最常见的写法但官方不推荐(容易把 worker 和线程生命周期搞混),明确否定它;再讲正解 worker-object 模式三件套:①worker 是普通 QObject 不给 parent;②moveToThread 搬家;③用信号槽通信。
【三件套逐条讲】线程亲和性用"每个 QObject 有户籍"类比;worker 模式用"雇外援干活,干完打电话汇报"类比,打电话就是信号槽;跨线程信号槽自动 Queued 是 Qt 的魔法所在(回指 Day2 的 Direct/Queued/Auto,闭环)。强调"不需要手动加锁",这是 worker 模式相比 std::thread+mutex 的核心优势。
【坑卡演示】在工作线程里直接 table->setItem,可能崩也可能"看起来正常"(又是 UB 双面性)。规则给死:"工作线程绝不碰 GUI,只发信号让主线程碰。"
【闭环验证】参考实现的离屏自测会自动校验"导入 3 条后总数 5 条、界面未阻塞",跑一次让全班看绿色通过。
【过渡】"最后一块拼图补上了,你们现在具备做一个完整 Qt 桌面软件的全部技能。下午最后一节,讲怎么把这些技能变成答辩成绩。" """
},
{
'badge': '下午 2 · 开发流程与规范 + 分组定方向',
'title': 'Git 提交习惯',
'boxes': [
('concept', None, [
'每完成一个任务就提交,提交粒度对应任务粒度',
'commit 信息说清「做了什么、为什么」——项目的可追溯记录,评审和回滚都靠它',
]),
('pitfall', None, [
'攒几天代码一次性提交、信息写"update"——历史失去可追溯性,出问题无法定位是哪次改动引入',
'从 Day6 第一次 commit 开始就按规范来',
]),
],
'minutes': 15, 'segment': '下午 · 流程规范',
'notes': """【讲法】用"代码是资产、commit 历史是资产的账本"类比,账本记得乱,资产就管不住。展示一个写得好的 commit 历史(每个消息动词开头、说清意图)和一个写得烂的(全是 update)的对比。
【规范给具体可执行标准】①粒度:一个任务一次提交;②消息格式:动词开头("添加"/"修复"/"重构"),一行说做了什么,必要时正文说为什么;③不要提交无法编译的代码。
【当堂练习(可选 10 分钟)】让每组在刚刚的案例代码上练习一次规范提交,这是明天起每天都要做的事,今天建立肌肉记忆。
【过渡】"代码写得好只是考核的一部分。下一页看清考核的全貌。" """
},
{
'badge': '下午 2 · 开发流程与规范 + 分组定方向',
'title': '考核标准与分组定方向',
'boxes': [
('concept', None, [
'考核不只看代码:项目管理(任务拆解/进度可见)+ 项目技术 + 职业素养(协作/答辩表达)',
'只埋头写代码会丢掉其他维度的分',
'今天散场前每组确认:项目选题 + 初步分工 + 任务拆解草稿(上午「需求拆解」那页就是模板)',
]),
('info', '兴趣钩子(各 5 分钟演示,选学)', [
'动效:QPropertyAnimation(任何 Q_PROPERTY 都能被缓动曲线驱动)',
'多状态 UIQStateMachine(状态成为一等对象,替代一堆 bool 标志)',
'线程的更高抽象:QtConcurrent / 线程池',
]),
],
'minutes': 25, 'segment': '下午 · 考核 + 分组',
'notes': """【考核讲法】投出考核标准三维度图,重点强调"技术只占三成",这是中初级学员最容易踩的认知坑(以为代码好就满分)。项目管理维度对应今天上午的"任务拆解",职业素养对应每天的协作和最后的答辩表达,把考核标准和今天讲的方法论一一对应。
【分组实操】留 15 分钟让每组完成三件事:①确认项目选题(参考方案书备选项目,或自拟);②初步分工(谁主界面、谁主数据、谁主协调);③用今天上午"需求拆解"的格式写一版任务拆解草稿。教师巡场逐组确认,散场前每组交一份。
【钩子讲法】三个钩子各 5 分钟演示即可,目的有二:①给学有余力的组指明加分方向(动画/状态机/QtConcurrent 都是答辩亮点);②告诉所有人"Qt 还有很多没讲的能力,项目期可以按需自学",留个引子。
【收尾承诺】明确告诉学员:明天起进入项目实作,工程师全程在场答疑;今天案例的全部代码(examples/student_manager/)对他们开放作参考。"""
},
{
'badge': '当日收尾 · 进入项目期',
'title': '当日产出验证 · 进入项目期',
'code': 'QT_QPA_PLATFORM=offscreen tools/run_qt.sh qt_student_manager\n# 自测 1/2:JSON 写入 → 清空 → 读回,2 条记录完整\n# 自测 2/2:后台线程导入 3 条,总计 5 条,界面未阻塞',
'bullets': [
'每组提交:项目选题 + 分工说明 + 任务拆解草稿',
'明天 Day6 起:学员编码 + 工程师指导',
'Day10 下午:项目答辩',
],
'minutes': 5, 'segment': '收尾',
'notes': """【验收】跑参考实现的离屏自测,全班看两行绿色通过;每组当场提交三件套(选题/分工/拆解草稿)。
【结训语】"五天前你们刚进门,今天你们已经能独立做一个会存数据、不卡界面、有规范流程的桌面软件。后面五天,把它变成你们自己的项目。Day10 答辩见。"散场。"""
},
]