Files
scuc-qt-course/tools/teaching_ppt/day3.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

191 lines
14 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 -*-
"""Day3 · QMainWindow / QDialog / 布局与控件 / QSS —— 页面对应 slides/day3.html。"""
SLIDES = [
{
'kind': 'title',
'badge': 'C++/Qt 方向实训 · 第 3 / 4 个讲授日',
'title': 'Day 3 · QMainWindow / QDialog / 布局与控件',
'subtitle': '主窗口骨架 / 对话框交互 / 布局讲透 / 常用控件 / QSS 美化',
'bullets': [
'上午 08:3011:00 讲授:QMainWindow · 对话框 QDialog · 标准对话框',
'上午 11:00–12:00 课堂练习:计算器套上 QMainWindow(菜单 + 停靠窗)',
'下午 13:30–16:00 讲授:布局管理器(回收脚手架)· 常用控件与 .qrc · QSS',
'下午 16:0017:00 作业:QSS 美化计算器 + 缩放自适应',
],
'minutes': 3, 'segment': '开场',
'notes': """【主线】今天全天围绕一件事:把昨天的计算器升级成"正经软件"。上午给骨架(主窗口加对话框),下午管排版和外观。开场就展示教师版的"升级后计算器"成品,让学员看到今天结束时自己的程序长什么样,目标可视化。
【时长骨架】上午:QMainWindow 30 → QDialog 模态非模态 30 → 标准对话框 15,讲授约 75 分钟,剩余给随堂跟敲和答疑。下午:布局 30 → 控件与 qrc 25 → QSS 25,随堂练穿插。
【衔接】昨天的"堆上建控件"军规今天在对话框一节会第三次出现,留意把这条线讲成呼应而不是重复。"""
},
{
'badge': '上午 · 讲授 08:3011:00',
'title': 'QMainWindow:标准桌面软件的骨架',
'boxes': [
('concept', None, [
'QMainWindow 不是「大号 QWidget」:菜单栏/工具栏/状态栏/停靠窗各就其位的固定区域协议',
'中心部件必须 setCentralWidget() 显式安放',
'菜单项和工具栏按钮的统一抽象是 QAction——一个动作,多处复用',
]),
('pitfall', None, [
'不设中心部件直接摆控件——布局行为不可预期',
'QAction 显示出来 ≠ 生效:忘 connect 就是「菜单点了没反应」',
]),
],
'source': '出处:p03/ch05/central_widget.cpp · dock_widget.cpp',
'minutes': 30, 'segment': '上午 · QMainWindow',
'notes': """【引入】投一张学员都认识的软件截图(VS Code / WPS),指认五个区域:菜单/工具/状态/中心/停靠。"QMainWindow 就是这套行业标准布局的现成实现。"
【讲法】跑 central_widget.cpp 与 dock_widget.cpp,边跑边对照代码:setCentralWidget、menuBar()->addMenu、addToolBar、statusBar()->showMessage 四个入口各演示一次。
【QAction 重点】用"一个动作三个入口"讲:同一个 QAction 同时挂菜单和工具栏,禁用它两处一起变灰,这就是抽象的价值。坑卡里"菜单点了没反应"发生率极高,让学员现在就形成"建 action 后第一件事 connect"的顺序习惯。
【互动】问:"中心部件只能有一个,那复杂界面怎么办?"答案是中心部件本身可以是任意组合控件(下午布局课解决),先埋问题。
【过渡】"主窗口是常驻界面;临时冒出来问一句的界面叫对话框。" """
},
{
'badge': '上午 · 讲授 08:3011:00',
'title': 'QDialog:模态与非模态',
'boxes': [
('concept', None, [
'模态 exec():阻塞等结果,返回值就是用户的选择(Accepted/Rejected',
'非模态 show():立即返回,窗口独立存活——对象必须活过当前函数作用域',
'非模态三种写法:堆 + parent / WA_DeleteOnClose 自清理 /(反例)栈',
]),
('pitfall', '常见的坑(所有权军规第三次出现!)', [
'非模态对话框建在栈上:show() 完函数一返回就析构——窗口一闪而过',
'p03/ch06 的三个 modeless 版本就是这个坑的对照教材',
]),
],
'source': '出处:p03/ch06/modal_dialog.cpp · modeless_dialog_stack.cpp · _heap.cpp · _deleteonclose.cpp',
'minutes': 30, 'segment': '上午 · QDialog',
'notes': """【讲法】模态/非模态先用生活类比定性:模态=收银台付款(不办完不能干别的),非模态=旁边开着的计算器(随时切换)。然后讲代码差异的本质:exec() 内部自己转事件循环所以能阻塞,show() 只是显示。
【三连跑对照】依次跑 ch06 三个 modeless 文件:栈版一闪而过(学员先猜原因,Day2 军规该有人能答上)、堆版正常但要考虑谁删、DeleteOnClose 版关窗自清理。同一功能三种生命周期策略并排,是全仓库最好的所有权教材。
【收束】黑板军规更新:"给了 parent 的控件必须 new""非模态对话框必须活过函数"。本质是同一条:对象的生命周期必须覆盖它的使用周期。
【过渡】"自己写对话框之前,先看看 Qt 送了哪些现成的。" """
},
{
'badge': '上午 · 讲授 08:3011:00',
'title': '标准对话框速览',
'code': 'auto ret = QMessageBox::question(this, "确认", "真的要删除吗?");\nif (ret == QMessageBox::Yes) { /* ... */ }\n\nQString path = QFileDialog::getOpenFileName(this, "选择文件", "", "*.json");\nif (!path.isEmpty()) { /* ... */ } // 用户可能点取消:必须处理空返回',
'boxes': [
('pitfall', None, [
'不处理「用户点了取消」(空路径/Rejected)就直接往下用——健壮性问题高发点',
]),
],
'source': '出处:p03/ch06/message_box_question.cpp · message_box_custom.cpp · file_dialog.cpp',
'minutes': 15, 'segment': '上午 · 标准对话框',
'notes': """【讲法】两个静态函数各演示一次即可,重点全放在坑卡上:让学员齐声回答"getOpenFileName 之后第一行代码写什么?"判空。用户永远可能点取消,这是健壮性思维的第一课(Day4 的 open() 判返回值、ParseError 检查是同一条思维线)。
【提示】QMessageBox 的 question/information/warning/critical 四兄弟提一嘴图标差异即可,不逐个演示。
【过渡】"骨架和对话框都有了。上午最后一小时,把你的计算器装进这副骨架。" """
},
{
'badge': '上午 · 课堂练习 11:00–12:00(工程师指导、答疑)',
'title': '任务卡 1 · 给计算器套上 QMainWindow',
'boxes': [
('task', '任务卡 1', [
'目标:昨天的计算器改造为 QMainWindow 应用;加菜单「帮助→关于」弹 QMessageBox;加「历史」停靠窗(可先放空 QLabel)',
'验收:计算器功能不回退;菜单动作有响应;停靠窗可拖动/收起',
'参考:p03/ch05/central_widget.cpp · dock_widget.cpp · p03/ch06/message_box_question.cpp',
]),
],
'minutes': 60, 'segment': '上午 · 课堂练习',
'notes': """【关键改造思路(先讲 3 分钟再放手)】昨天的计算器是一个 QWidget,今天不重写,把它整个 setCentralWidget 进 QMainWindow。这个"旧组件装进新骨架"的动作本身就是组件化思维的第一次实践,点破它。
【巡场检查点】①菜单 QAction 忘 connect(上午坑卡现场版,让学员自己对照坑卡定位);②功能回退(改造弄坏了昨天的计算逻辑,借机讲"改结构不改逻辑"的重构纪律)。
【分层】快的学员让"历史"停靠窗真的记录每次运算(QLabel 追加文本即可),这直接为 Day5 案例的表格记录做铺垫。
【收尾】抽 1–2 人演示:点菜单弹关于、拖动停靠窗。"""
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '布局管理器(回收昨天的脚手架)',
'boxes': [
('concept', None, [
'布局接管子控件位置/大小计算,随窗口缩放自动重排',
'QHBox / QVBox / QGrid 三兄弟 + 嵌套组合 + 伸缩因子 addStretch()',
'布局对象同样遵循 QObject 父子所有权',
]),
('pitfall', None, [
'同一控件不能属于两个布局',
'布局设置后又用 setGeometry()/move() 手动改位置——被下次刷新覆盖,看起来「设置没生效」',
]),
],
'source': '出处:p03/ch08/custom_widget.cpp',
'minutes': 30, 'segment': '下午 · 布局',
'notes': """【兑现承诺】开场明确"昨天欠的布局课现在还":把计算器脚手架里的 QGridLayout 拿出来讲原理。为什么 addWidget(w, row, col) 之后拉伸窗口按钮会跟着变?因为布局在每次 resize 时重算所有子控件的几何。
【讲法】演示三兄弟各一个最小例子,然后重点讲嵌套:垂直布局套水平布局,拼出"上显示屏下键盘"的计算器结构。学员昨天已经无意识用过,今天补上心智模型。addStretch 用"弹簧"类比。
【坑卡】当堂演示"布局后 move() 失效":move 完拉伸一下窗口,控件跳回布局位置,"看起来没生效"的真相是被布局覆盖。规则:进了布局就别手动定位,二选一。
【随堂练 10 分钟】把自己计算器的布局改成"显示屏占 1 份、键盘占 3 份"的比例(伸缩因子)。
【过渡】"位置管好了,看控件本身还有什么花样。" """
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': '常用控件 + 资源系统 .qrc',
'boxes': [
('concept', None, [
'QLabel 一个控件多种形态:纯文本 / 富文本 HTML / 图片 QPixmap / 动图 QMovie',
'输入靠 QLineEdit;按钮族 QPushButton / QRadioButton / QCheckBox',
':/... 路径是 Qt 资源系统:资源编译期打包进可执行文件,发布不用带散图',
]),
('pitfall', None, [
'.qrc 路径与代码引用不一致:运行时静默失败(图不显示),不报编译错误',
'忘接 CMake 的 AUTORCC / qt5_add_resources——资源系统不生效',
]),
],
'source': '出处:p03/ch08/label_text_html.cpp · label_pixmap.cpp · label_movie.cpp',
'minutes': 25, 'segment': '下午 · 控件与资源',
'notes': """【讲法】三个 label 示例连跑(文本→HTML→图片/动图),节奏快一点,每个 5 分钟:控件 API 本身不难,难的是"知道有什么可用",本页目标是建目录索引不是背 API。
【qrc 重点】打开 label_pixmap.qrc 和对应 cpp 对照讲:qrc 里登记路径 → 代码里 :/ 引用 → CMake AUTORCC 编译打包。坑卡演示:把代码里的 :/ 路径改错一个字母再跑,不报错、图消失,"静默失败"要亲眼见过才会警惕(Day4 的 open()/ParseError 静默失败是同一主题)。
【互动】问:"图片放资源系统 vs 放程序旁边的目录,各适合什么?"只读小资源进 qrc;用户数据/大文件放文件系统(Day4 的主题,顺势预告)。
【过渡】"功能齐了,长得还不行。最后一节,化妆。" """
},
{
'badge': '下午 · 讲授 13:3016:00',
'title': 'QSS 界面美化',
'code': 'QPushButton#okButton { background-color: #2f855a; border-radius: 6px; }\nQPushButton#okButton:hover { background-color: #276749; }',
'boxes': [
('concept', None, [
'语法脱胎于 CSS:类型选择器 / ID 选择器(#objectName/ 伪状态(:hover :pressed+ 层叠',
'作用对象是 QWidget 树而非 DOM;「长什么样」与「做什么」分离,改样式不碰逻辑',
]),
('pitfall', None, [
'ID 选择器必须先 setObjectName()',
'setStyleSheet() 级联影响所有子控件——范围失控最易踩,样式尽量写窄(带选择器)',
]),
],
'source': '出处:examples/qss_styling/qt_qss_styling.cppREADME 有练习方向)',
'minutes': 25, 'segment': '下午 · QSS',
'notes': """【讲法】跑 qss_styling 示例,然后当堂改样式立即重跑,"改外观不碰逻辑"的分离感是本节要传达的核心体验。学过 CSS 的学员点一句"就是 CSS 的方言",没学过的按"选择器=谁、属性=怎么画"从零讲。
【坑卡演示】级联失控:在顶层 widget 上裸写 background-color(不带选择器),跑起来所有子控件全变色,然后加上类型选择器收窄。让学员记"样式写窄"三个字。
【伪状态】:hover/:pressed 是"零代码交互反馈",性价比极高,今晚作业的验收点之一。
【随堂练 15 分钟】给上午的主窗口版计算器套第一版 QSS(哪怕只改按钮底色),作业时段继续精修。
【过渡】"今晚作业就是把它做完做好,验收标准在下一页。" """
},
{
'badge': '下午 · 课堂作业 16:00–17:00(工程师答疑)',
'title': '任务卡 2 · 用 QSS 美化计算器',
'boxes': [
('task', '任务卡 2', [
'目标:数字键/运算键/等号键用 QSS 区分颜色;整体布局随窗口缩放自适应',
'验收:至少 2 类按钮不同 background-color;至少 1 个按钮有 :hover 或 :pressed',
'验收:拉伸窗口按钮跟着重排',
'参考:examples/qss_styling/ · p03/ch08/custom_widget.cpp',
]),
],
'minutes': 60, 'segment': '下午 · 作业',
'notes': """【实现提示(开工前讲 3 分钟)】三类按键区分的正确姿势:按类别 setObjectName(或用动态属性),QSS 里 ID/属性选择器分别上色。不要给每个按钮单独 setStyleSheet(层级混乱且难维护)。
【巡场检查点】①样式写太宽把显示屏也染色了(下午坑卡现场版);②hover 无效,大概率 objectName 没设或拼错,静默失败,让学员自己按坑卡排查。
【分层】快的学员挑战"深色主题"整套配色,或给等号键加 :pressed 按下变暗的两态反馈。
【预告】明天计算器线暂停一天(事件/绘图/文件),Day5 综合案例会用到今天所有的骨架加布局加美化技能。"""
},
{
'badge': '当日收尾',
'title': '当日产出验证',
'bullets': [
'现场演示:主窗口版计算器(菜单/停靠窗)+ QSS 配色 + 缩放自适应,功能不回退',
'钩子:QSS 与前端 CSS 同源;Qt Designer / QML 是另一条 UI 路线(知道存在即可)',
'明天 Day4:事件机制 / 绘图 / 表格 + 文件系统——数据要开始落盘了',
],
'minutes': 5, 'segment': '收尾',
'notes': """【验收】抽 2–3 组演示升级后的计算器,其他人对照验收标准自查打钩。
【钩子讲法】一句话安放 Designer/QML"拖拽和声明式是另外两条 UI 路线,本课程走纯代码是为了让你们看清每一层。会走路了再学开车不迟。"
【收线】"三天了,你们的程序还没碰过磁盘。明天开始,让数据活过程序的重启。"散场。"""
},
]