# Day2–4 课堂练习 + 课后作业（分层设计）

> 配套 [`OUTLINE_4DAY.md`](OUTLINE_4DAY.md) 的任务卡、`slides/day2.html`…`day4.html`
> 里的「练/作业」小节。本文给出可直接打印/投屏的作业单：每项作业拆成
> **基础版（必做，验收线）** + **挑战版（选做，见能力发挥）** 两层，时间预算按
> 「基础版 1 小时内可完成、挑战版累计不超过 2 小时」控制，照顾基础不理想的
> 学员——**先保基础版全员过关，挑战版不做不扣分**。

## 分层设计原则

1. **基础版是及格线，不是引导线**：题面本身已经是当天知识点的最小闭环，做完
   基础版=当天目标达成；工程师答疑优先保基础版全员通过。
2. **挑战版是同一个程序的自然延伸**，不引入新知识点，只要求「更完整/更健壮/
   更贴近真实使用」——强度落在「把已学的东西用得更彻底」，不是提前预习明天内容。
3. **验收用现场提问代替翻源码**：给几组输入让学生当场演示结果（下表「验收用例」），
   比对照代码更能测出是真理解还是照抄。
4. 每天的作业首尾相接（计算器 → 计算器 QMainWindow 化 + 美化 → 数据存储工具），
   挑战版做不完不影响第二天用基础版继续。

---

## Day2 下午 · 简易计算器

对应任务卡 2（`OUTLINE_4DAY.md` Day2 下午作业）。教师参考实现：
[`examples/calculator/`](examples/calculator/README.md)（代码布局版）、
[`examples/calculator_designer/`](examples/calculator_designer/README.md)（Qt Designer 对照版）。

| | 要求 | 时间预算 |
|---|---|---|
| **基础版（必做）** | 数字键 0–9 + 至少一种运算符（+/-/×/÷ 任选其一）+ 等号，能正确显示两个数的运算结果；界面不重叠、按钮可点击；`connect` 全部用新语法且写在构造函数里 | ≈1h |
| **挑战版（选做）** | 补全四则运算全部支持；小数点；清空键 `C`；退格键 `⌫`；除零不崩溃、显示 `Error` 且下一次输入自动清空；连续按运算符实现链式计算（如 `2+3+4=` 得 `9`） | 累计 ≤2h |

**验收用例**（现场让学生当场演示，不看代码）：

- 基础版：`7 + 3 =` → `10`；`9 - 4 =` → `5`（任选一种运算符即可）
- 挑战版：`6 ÷ 0 =` → `Error`；`4.5 + 0.5 =` → `5`；连续输入 `12` 后按退格 → `1`；
  `2 + 3 + 4 =` → `9`

**给基础不理想同学的提示**：卡在"点击没反应"先查 `connect` 有没有漏写、按钮有没有
真的加到布局里；卡在"数字显示不对"先只处理个位数，两位数以上的拼接逻辑
（`display->text() + 新数字`）留到基础版跑通个位数运算之后再加。

---

## Day3 上午 · 计算器 QMainWindow 化

对应 `OUTLINE_4DAY.md` Day3 上午课堂练习（11:00–12:00）。

参考实现：[`examples/calculator_mainwindow/`](examples/calculator_mainwindow/README.md)
（以 Day2 增强版 [`calculator_designer_ext/`](examples/calculator_designer_ext/README.md)
为起点；`CalculatorDesigner` 是独立 `QWidget`，`setCentralWidget` 装进 `QMainWindow`，
核心求值逻辑不动，仅加 `evaluated` 信号供历史停靠窗订阅）。

| | 要求 | 时间预算 |
|---|---|---|
| **基础版（必做）** | 把 Day2 计算器的键盘 `setCentralWidget` 装进 `QMainWindow`；加菜单栏「帮助→关于」，点击弹出 `QMessageBox::about` 显示一句自我介绍 | ≈40min |
| **挑战版（选做）** | 加一个「历史」停靠窗（`QDockWidget` + `QListWidget`）：每次按等号，把这次的算式和结果（如 `7+3=10`）追加成一条历史记录；停靠窗可拖动/浮动/关闭（`QDockWidget` 默认行为，不用额外写代码） | ≤1h |

**验收用例**：点菜单「帮助→关于」能弹框；挑战版——连续算 3 次不同的算式，历史
停靠窗里能看到 3 条记录且内容正确。

**给基础不理想同学的提示**：`QMainWindow` 不是 `QWidget` 的替代品，是新建一个
`QMainWindow` 子类，把原来计算器的键盘部分（`QWidget` 或其内容）通过
`setCentralWidget()` 塞进去；菜单点了没反应先检查 `QAction` 是不是忘了 `connect`
（Day3 上午刚讲的坑卡）。挑战版做不完，历史停靠窗留空 `QLabel` 占位即可，
不影响 Day3 下午继续。

---

## Day3 下午 · 计算器美化（QSS）

对应任务卡（`OUTLINE_4DAY.md` Day3 下午作业）。

参考实现：[`examples/calculator_mainwindow_qss/`](examples/calculator_mainwindow_qss/README.md)
（Day3 上午版 + QSS 双主题；与上午版仅 `mainwindow.{h,cpp}` 差异，演示「美化与逻辑正交」）。

| | 要求 | 时间预算 |
|---|---|---|
| **基础版（必做）** | 数字键 / 运算键 / 等号键至少用 3 种不同颜色区分（`setObjectName` + QSS ID 选择器 `#objectName{...}`）；整体布局随窗口缩放不重叠、不错位 | ≈1h |
| **挑战版（选做）** | 至少 1 个按钮有 `:hover` 或 `:pressed` 视觉反馈；给结果显示框也套样式（圆角边框/字号）；额外做一个"深色主题"QSS 字符串，加一个按钮或菜单项用 `setStyleSheet()` 在两套样式间切换 | ≤1h |

**验收用例**：缩放窗口到很小和很大两种尺寸，按钮不重叠不错位；挑战版——点切换
主题按钮，界面颜色整体改变。

**给基础不理想同学的提示**：QSS 选择器认的是 `objectName`（`setObjectName("btnEquals")`
之后才能用 `#btnEquals{...}`），忘设置 `objectName` 是"样式写了但没生效"的头号原因；
先做好 1 种颜色区分再扩展到 3 种，不要一次性套一大段 QSS 上去调不出问题在哪。

---

## Day4 上午 · 事件过滤器 / 自定义绘图

对应 `OUTLINE_4DAY.md` Day4 上午课堂练习（11:00–12:00）。

| | 要求 | 时间预算 |
|---|---|---|
| **基础版（必做，二选一）** | ① 给某个控件装 `installEventFilter()`，鼠标按下时在控制台打印坐标；<b>或</b> ② 重写 `paintEvent`，用 `QPainter` 画一个组合图形（棋盘格、简单进度条、或几条交叉线均可） | ≈40min |
| **挑战版（选做，两项整合）** | 在一个自定义 `QWidget` 上：鼠标点击时记录点击坐标（`mousePressEvent` 或事件过滤器都行）到一个 `QVector<QPoint>`，每次点击后 `update()` 触发重绘，`paintEvent` 里把历史上所有点击过的坐标都画出来（比如画小圆点）——点越点越多，图案累积不丢失 | ≤1h |

**验收用例**：基础版①——点击窗口能在控制台看到坐标打印；基础版②——窗口能看到
一个图形；挑战版——连续点击 5 个不同位置，5 个点都保留在画面上（不是只显示
最后一个）。

**给基础不理想同学的提示**：基础版明确「二选一」，不要求两项都做——事件拦截和
自定义绘图是两条独立线索，先吃透一条。忘调用 `update()` 是"画了但界面没刷新"
的头号原因（`paintEvent` 只在 Qt 认为需要重绘时才会被调用，手动改了数据必须
自己触发 `update()`）。

---

## Day4 下午 · 本地数据存储小工具

对应任务卡（`OUTLINE_4DAY.md` Day4 下午作业）。这是 Day5 综合案例
[`examples/student_manager/`](examples/student_manager/README.md) 的直接前身，
教师验收时可参考其数据层设计（不要求学生做到那个完整度）。

| | 要求 | 时间预算 |
|---|---|---|
| **基础版（必做）** | 「学员信息」表单（姓名/成绩/标签，3 个 `QLineEdit` + 「添加」按钮）把记录追加到列表控件（`QListWidget`/`QTableWidget` 均可）；「保存」按钮把全部记录写成 JSON 文件；重启程序后「读取」按钮能把之前保存的记录读回、字段不丢、顺序不乱（至少 2 条） | ≈1.5h |
| **挑战版（选做）** | ① 加「删除选中行」；② 数据文件路径用 `QCoreApplication::applicationDirPath()` 拼接，不用裸相对路径（当天★坑卡的实操版）；③ 读取时文件不存在/JSON 格式损坏要给出友好提示而不是崩溃或静默清空（检查 `open()` 返回值和 `QJsonParseError`） | ≤1h |

**验收用例**：添加 2 条记录 → 保存 → 关闭程序 → 重新打开 → 点「读取」→ 2 条记录
都在、字段值正确；挑战版——把 JSON 文件手动改坏一个字符再读取，程序不崩溃。

**给基础不理想同学的提示**：先固定用一个写死的文件名（比如 `data.json`）把
「写入→读回」跑通，再考虑路径是否规范（挑战版①）；`QJsonDocument::toJson()`/
`fromJson()` 是配对的两端，写的时候用什么结构（数组套对象），读的时候就按同样
结构解析回来，两边字段名要完全一致（区分大小写）。

---

## 给工程师/助教的验收操作提示

- 11:00–12:00、16:00–17:00 两个答疑窗口，**优先确认基础版验收用例**，挑战版
  时间不够可以留到当晚或第二天早上补交，不阻塞后续课程进度。
- 每天验收合格线 = 基础版全部用例通过；挑战版按完成比例加分，不设满分门槛。
- 若某学员基础版都吃力，参照各节「给基础不理想同学的提示」拆解成更小步骤，
  必要时可对照教师参考实现（`examples/calculator/`、`examples/calculator_designer/`）
  逐段讲解，但**不要直接给学生抄源码**——抄一次治标不治本，下一天同类问题会
  重复出现。
