# 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()`,鼠标按下时在控制台打印坐标; ② 重写 `paintEvent`,用 `QPainter` 画一个组合图形(棋盘格、简单进度条、或几条交叉线均可) | ≈40min | | **挑战版(选做,两项整合)** | 在一个自定义 `QWidget` 上:鼠标点击时记录点击坐标(`mousePressEvent` 或事件过滤器都行)到一个 `QVector`,每次点击后 `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/`) 逐段讲解,但**不要直接给学生抄源码**——抄一次治标不治本,下一天同类问题会 重复出现。