Files
scuc-qt-course/docs/teaching/examples/calculator_mainwindow/README.md
T
张宗平 05a685c952 Day3 教学示例:增强版计算器接入 + QMainWindow/QSS 两版参考实现
- calculator_designer_ext(Day2 增强版:表达式求值 + 括号,递归下降解析器)接入 cmake
- calculator_mainwindow(Day3 上午):增强版计算器装进 QMainWindow,
  菜单/状态栏/历史停靠窗,纯代码 + 显式 connect,与 mainwindow_showcase 形成坑卡对照
- calculator_mainwindow_qss(Day3 下午):上午版 + QSS 双主题美化(按键分色/伪状态/深浅切换)
- 验证 mainwindow_showcase(QMainWindow 六大组件综合演示,.ui + .qrc)
- OUTLINE_4DAY.md / ASSIGNMENTS.md Day3 衔接增强版起点 + 参考实现指引
- 补提交 Day2 calculator/ 与 calculator_designer/ 参考实现(CMakeLists 已引用)
- 全量构建 28/28,4 目标离屏自测退出码 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 22:59:05 +08:00

77 lines
4.1 KiB
Markdown
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.
# Day3 上午参考实现:计算器 → QMainWindow
> 对应教案 [`OUTLINE_4DAY.md`](../../OUTLINE_4DAY.md) Day3 上午「QMainWindow / 对话框
> QDialog」+ [`ASSIGNMENTS.md`](../../ASSIGNMENTS.md) Day3 上午课堂练习。手工维护,
> 非生成产物。
## 定位:把 Day2 增强版计算器装进 QMainWindow
以 Day2 增强版计算器 [`../calculator_designer_ext/`](../calculator_designer_ext/README.md)
`CalculatorDesigner``QWidget`,含表达式求值 + 括号)为**中心部件**,给它套上
`QMainWindow` 外壳:菜单栏 / 状态栏 / 历史停靠窗。**计算器面板本身的输入与求值逻辑
一字未改**——这正是「界面外壳升级、核心部件不动」的关注点分离。
## 与 Day2 增强版的差异(仅两处,集中在 `calculatordesigner.{h,cpp}`
| 改动 | 位置 | 目的 |
|---|---|---|
| 新增 `evaluated(expr, result)` 信号 | `calculatordesigner.h/.cpp` | 按 `=` 求值成功后发出,供历史停靠窗订阅(演示「给已有部件加信号,与外壳通信」) |
| 新增 `loadExpression(expr)` 方法 | `calculatordesigner.h/.cpp` | 把一条历史表达式回填到编辑区,配合历史双击 |
`expressionevaluator.{h,cpp}` / `widget.ui` 与 Day2 增强版完全一致(自包含复制,便于
学员单读 Day3 代码不跳目录)。
## 与 [`../mainwindow_showcase/`](../mainwindow_showcase/README.md) 的分工对照
两者同为 Day3 上午教学载体,风格刻意对立,覆盖不同教学诉求:
| | `mainwindow_showcase` | 本例 `calculator_mainwindow` |
|---|---|---|
| 形态 | 极简文本编辑器(`QTextEdit` 中心) | 计算器(`CalculatorDesigner` 中心) |
| 主窗体结构来源 | `.ui` 表单(Designer | 纯代码 |
| 信号槽 | `on_<obj>_<sig>` 自动连接 | 显式 `connect()` |
| 教学侧重 | QMainWindow 六大组件齐全(含资源 `.qrc`) | 作业线应用 + 显式 connect 坑卡 |
| 用法 | 教师讲「组件协议」时演示 | 学员课堂练习对照、作业验收参考 |
**坑卡对照**`mainwindow_showcase` 用自动连接,控件改名静默失效;本例用显式 `connect`
`QAction` 忘 connect 时菜单点了没反应——两种「静默失败」在同一节课形成对照。
## 教学要点(对应 OUTLINE_4DAY.md Day3 上午)
1. **中心部件必须 `setCentralWidget`**`mainwindow.cpp` 构造函数里 `setCentralWidget(m_calc)`
直接往 `QMainWindow``addWidget` 是 Day3 上午坑卡——布局行为不可预期。
2. **菜单 + QAction + 显式 connect**`buildMenu()` 逐菜单逐 `QAction` 建,每个都 `connect`
`m_actAbout` 若忘 `connect`,菜单显示正常、点了没反应。
3. **模态对话框**`onAbout()``QMessageBox::about`,内部 `exec()` 阻塞等关闭。
4. **非模态停靠窗**`QDockWidget` + `addDockWidget(Qt::RightDockWidgetArea, ...)`
可拖动 / 浮动 / 关闭(`QDockWidget` 默认行为)。与 `p03/ch06` 三个 modeless 对照文件
呼应「非模态对象必须活过函数作用域」——停靠窗是 `new` 在堆上 + parent=`this`
5. **信号槽跨对象通信**`CalculatorDesigner::evaluated``MainWindow::onEvaluated`
外壳订阅核心部件的信号,二者解耦。
## 构建与运行
```bash
# 构建(仓库根;Qt 5.14.2
cmake --build build --target qt_calculator_mainwindow
# 离屏自测(9 组计算用例 + 历史追加/双击回填/清空,退出码 0 即全过)
PATH=E:/Qt/Qt5.14.2/5.14.2/mingw73_64/bin:$PATH \
QT_QPA_PLATFORM=offscreen \
./build/docs/teaching/examples/qt_calculator_mainwindow.exe; echo "exit=$?"
# 有 GUI 环境直接看窗口
./build/docs/teaching/examples/qt_calculator_mainwindow.exe
```
## 自测用例
`MainWindow::runSelfTest()`offscreen 自动执行)覆盖:
- Day2 全部 9 组计算用例(验证升级外壳后计算行为不变)
- 按等号后历史停靠窗追加 `"expr = result"` 条目
- 除零 `Error` **不**进历史(`evaluated` 只在求值成功时 emit
- 历史双击 → 表达式回填到编辑区
- 「清空历史」后列表为空
- 「关于」action 已创建并显式连接(GUI 弹窗验证靠现场演示)