# Day 3

### QMainWindow / 对话框 QDialog / 布局管理器 / 常用控件

C++/Qt 方向实训 · 第 3 / 4 个讲授日

- 上午（08:30–11:00 讲 / 11:00–12:00 练）：QMainWindow · 对话框 QDialog
- 下午（13:30–16:00 讲 / 16:00–17:00 作业）：布局管理器 · 常用控件 · QSS 美化

今天的主线：把昨天的计算器升级成"正经软件"——主窗口骨架 + 对话框交互 + 布局讲透 + 美化。

上午 · 讲授 08:30–11:00

## QMainWindow / 对话框 QDialog 讲

### QMainWindow：标准桌面软件的骨架 讲

#### 核心概念

- QMainWindow 不是"大号 QWidget"：它有固定的区域布局协议——菜单栏 / 工具栏 / 状态栏 / 停靠窗（dock）各就其位，中心部件必须 `setCentralWidget()` 显式安放；菜单项和工具栏按钮的统一抽象是 `QAction`（一个动作， 多处复用）。

#### 常见的坑

- 直接往 QMainWindow 上摆控件而不设中心部件——布局行为不可预期；
- `QAction` 显示出来 ≠ 生效：忘了 `connect` 到槽， 就是"菜单点了没反应"。

出处：p03/ch05/central_widget.cpp · dock_widget.cpp

### QDialog：模态与非模态 讲

#### 核心概念

- 模态 `exec()`：阻塞等用户给结果，返回值就是用户的选择 （Accepted/Rejected）；非模态 `show()`：立即返回，窗口独立存活 ——所以对话框对象必须活过当前函数作用域（堆 + parent，或 `WA_DeleteOnClose` 自清理）。
- 标准对话框拿来就用：`QMessageBox`（提示/确认）、`QFileDialog`（选文件）。

#### 常见的坑（昨天的所有权坑第三次出现！）

- 非模态对话框建在栈上：`show()` 完函数一返回就析构——窗口一闪而过。 `p03/ch06` 的三个 modeless 版本（栈/堆/DeleteOnClose）就是这个坑的对照教材。

出处：p03/ch06/modal_dialog.cpp · modeless_dialog_stack.cpp · modeless_dialog_heap.cpp · modeless_dialog_deleteonclose.cpp

### 标准对话框速览 讲

    auto ret = QMessageBox::question(this, "确认", "真的要删除吗？");
    if (ret == QMessageBox::Yes) { /* ... */ }

    QString path = QFileDialog::getOpenFileName(this, "选择文件", "", "*.json");
    if (!path.isEmpty()) { /* ... */ }   // 用户可能点取消：必须处理空返回

#### 常见的坑

- 不处理"用户点了取消"（空路径/Rejected）就直接往下用——健壮性问题的高发点。

出处：p03/ch06/message_box_question.cpp · message_box_custom.cpp · file_dialog.cpp

上午 · 课堂练习 11:00–12:00（工程师指导、答疑）

## 动手：计算器主窗口化 练

#### 任务卡 1 · 给计算器套上 QMainWindow 练

目标
  把昨天的计算器改造为 QMainWindow 应用：加菜单「帮助→关于」弹 `QMessageBox`；加一个「历史」停靠窗（可先放空 QLabel）
验收标准
  计算器功能不回退；菜单动作有响应；停靠窗可拖动/收起
基础知识 · 参考
  p03/ch05/central_widget.cpp · dock_widget.cpp · p03/ch06/message_box_question.cpp

下午 · 讲授 13:30–16:00

## 布局管理器 / 常用控件 / QSS 美化 讲

### 布局管理器（回收昨天的脚手架） 讲

#### 核心概念

- 布局管理器接管子控件的位置/大小计算，随窗口缩放自动重排——昨天计算器里"先用后学" 的 `QGridLayout` 今天讲透：QHBox/QVBox/QGrid 三兄弟 + 嵌套组合 + 伸缩因子 `addStretch()`；布局对象同样遵循 QObject 父子所有权。

#### 常见的坑

- 同一控件不能属于两个布局；
- 布局设置后又用 `setGeometry()`/`move()` 手动改位置—— 会被下次布局刷新覆盖，看起来"设置没生效"。

出处：p03/ch08/custom_widget.cpp

### 常用控件 + 资源系统 .qrc 讲

#### 核心概念

- QLabel 一个控件三种形态：纯文本 / 富文本 HTML / 图片（`QPixmap`）/ 动图（`QMovie`）；输入靠 QLineEdit，按钮族 QPushButton/QRadioButton/QCheckBox。
- `:/...` 路径是 Qt 资源系统：资源编译期打包进可执行文件，运行时不依赖 外部文件——发布不用带一堆散图。

#### 常见的坑

- `.qrc` 里的路径与代码引用不一致：运行时静默失败（图不显示）， 不报编译错误；忘接 CMake 的 AUTORCC/`qt5_add_resources` 资源系统不生效。

出处：p03/ch08/label_text_html.cpp · label_pixmap.cpp · label_movie.cpp

### QSS 界面美化 讲

    QPushButton#okButton { background-color: #2f855a; border-radius: 6px; }
    QPushButton#okButton:hover { background-color: #276749; }

#### 核心概念

- 语法脱胎于 CSS：类型选择器 / ID 选择器（`#objectName`）/ 伪状态 （`:hover` `:pressed`）+ 层叠继承——作用对象是 QWidget 树而非 DOM； "长什么样"与"做什么"分离，改样式不碰逻辑。

#### 常见的坑

- ID 选择器必须先 `setObjectName()`；
- `setStyleSheet()` 会级联影响所有子控件——范围失控是最容易踩的坑， 样式尽量写窄（带选择器），不要裸写属性。

出处：examples/qss_styling/qt_qss_styling.cpp （README）

下午 · 课堂作业 16:00–17:00（工程师答疑）

## 作业：计算器美化升级 练

#### 任务卡 2 · 用 QSS 美化计算器 练

目标
  数字键/运算键/等号键用 QSS 区分颜色；整体布局改为随窗口缩放自适应
验收标准
  至少 2 类按钮有不同 `background-color`；至少 1 个按钮有 `:hover` 或 `:pressed` 效果；拉伸窗口按钮跟着重排
基础知识 · 参考
  examples/qss_styling/ · p03/ch08/custom_widget.cpp

## 当日产出验证

#### 验收

现场演示升级后的计算器：主窗口（菜单/停靠窗）+ QSS 配色 + 缩放自适应，功能不回退。

返回目录 · ← 上一天 · 下一天 →