# QMainWindow 教学综合示例(qt_mainwindow_showcase)
覆盖川大 wiki「5 QMainWindow」(pageId 58954529,https://wiki.suncaper.net/display/scu2023/5+QMainWindow)
的 **5.1–5.6 全部六大核心组件**,形态为**极简文本编辑器**(`QTextEdit` 中心部件),
纯组件演示、**无文件 I/O**。主窗体结构用 **Qt Designer(`.ui`)** 设计,应用图标与动作图标
走 **资源文件(`.qrc`)**。
> 仓库已有 `p03/ch05/` 的零散单组件示例(central_widget、dock_widget);本示例是
> **综合演示**,把六者整合到一个 `QMainWindow` 程序里,并能交互操控它们。
## 六大组件 ↔ 代码映射
| wiki | 组件 | 代码位置 | 演示的 API |
|---|---|---|---|
| 5.5 | 中心部件 | `mainwindow.ui`:`centralwidget/textEdit`;`.cpp`:`textChanged`/`cursorPositionChanged → updateStatusBar` | `setCentralWidget(QTextEdit)` |
| 5.1 | 菜单栏 | `.ui`:`menubar` + `menuFile/menuView/menuHelp/menuToolBarArea` + 12 个 `QAction`;`.cpp`:`on_action*_triggered/toggled` | `menuBar()`/`addMenu`/`QAction` |
| 5.2 | 工具栏 | `.ui`:`mainToolBar`(复用 `QAction`);`.cpp`:`setMovable`/`setAllowedAreas`/`setVisible` | `addToolBar`/`setAllowedAreas`/`setMovable` |
| 5.3 | 状态栏 | `.cpp`:`addPermanentWidget(statusPosLabel)`/`showMessage` | `addWidget`/`insertWidget`/`removeWidget`(见下) |
| 5.4 | 浮动窗口 | `.ui`:`dockWidget`;`.cpp`:`setFloating`/`setAllowedAreas` | `QDockWidget`/`addDockWidget` |
| 5.6 | 资源文件 | `mainwindow.qrc` + `.ui` 的 `windowIcon` + 各 `QAction` 图标 | `:/icons/*.png` |
## 关键设计点
### 1. QAction 是菜单项与工具按钮的公共抽象(5.1)
12 个 `QAction` 在 `.ui` 的 `MainWindow` widget 内**定义一次**,被菜单和工具栏**共同引用**
(``)——这正是 wiki 强调的「同一动作,加进菜单显示为菜单项、
加进工具栏显示为工具按钮」。例如 `actionNew` 同时出现在「文件」菜单和主工具栏。
### 2. 信号槽:on_\_\ 自动连接
槽命名遵循 Qt 约定,由 `setupUi()` 触发的 `QMetaObject::connectSlotsByName` **自动连接**,
无需手写 `connect`(KISS)。例如 `on_actionToggleDockFloating_triggered()` 自动连接
`actionToggleDockFloating` 的 `triggered` 信号。
### 3. 视图菜单 = 「操控」各组件的入口
| 动作 | 行为 | 对应组件/API |
|---|---|---|
| 显示主工具栏 ✓ | `mainToolBar->setVisible(checked)` | 5.2 |
| 显示状态栏 ✓ | `statusBar()->setVisible(checked)` | 5.3 |
| 工具栏可移动 ✓ | `mainToolBar->setMovable(checked)` | 5.2 `setMovable` |
| 停靠窗口浮动/停靠 | `dockWidget->setFloating(!isFloating())` | 5.4 `setFloating` |
| 显示浮动窗口 | `dockWidget->show()` | 5.4 重新显示(被 X 关闭后找回) |
| 关闭浮动窗口 | `dockWidget->hide()` | 5.4 直接隐藏 |
| 工具栏停靠区(子菜单 5 项) | `mainToolBar->setAllowedAreas(...)` | 5.2 `setAllowedAreas` |
### 4. 状态栏 API(5.3)
运行时演示 `addPermanentWidget`(永久「行:列 字数」标签)+ `showMessage`(动作临时提示)。
其余三 API 的等价用法(教学参考):
```cpp
// addWidget:在状态栏左侧添加普通部件(非永久,会挤占临时消息区)
statusBar()->addWidget(new QLabel("就绪"));
// insertWidget:在 index 位置插入部件
statusBar()->insertWidget(0, new QLabel("插入"));
// removeWidget:移除某部件
statusBar()->removeWidget(someLabel);
```
## 目录结构
```
mainwindow_showcase/
├── mainwindow.ui # Designer 表单:主窗体结构 + 12 个 QAction
├── mainwindow_showcase.h/.cpp # MainWindow 类(动态行为)
├── main.cpp # QApplication + offscreen 自动退出
├── mainwindow.qrc # 资源清单(5 个图标)
├── gen_icons.py # 图标生成脚本(标准库,无依赖)
├── icons/ # app/new/quit/about/dock.png(gen_icons.py 产物)
└── README.md # 本文档
```
## 构建与运行(Windows + MinGW 7.3.0 + Qt 5.14.2)
> 必须用与 Qt 5.14.2 ABI 匹配的 mingw730_64(`E:/Qt/Qt5.14.2/Tools/mingw730_64`),
> 不可用 PATH 中可能存在的更高版本 g++。
```bash
# 1) 生成图标
python docs/teaching/examples/mainwindow_showcase/gen_icons.py
# 2) 配置(在仓库根;本示例随 docs/teaching/examples 一起被顶层 CMakeLists 收录)
cmake -S . -B build_mw -G "MinGW Makefiles" \
-DCMAKE_PREFIX_PATH=E:/Qt/Qt5.14.2/5.14.2/mingw73_64 \
-DCMAKE_CXX_COMPILER=E:/Qt/Qt5.14.2/Tools/mingw730_64/bin/g++.exe \
-DCMAKE_C_COMPILER=E:/Qt/Qt5.14.2/Tools/mingw730_64/bin/gcc.exe
# 3) 构建单目标
cmake --build build_mw --target qt_mainwindow_showcase
# 4) 运行(GUI)
PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/bin:$PATH \
QT_QPA_PLATFORM_PLUGIN_PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/plugins/platforms \
./build_mw/docs/teaching/examples/qt_mainwindow_showcase.exe
# 5) 离屏自动退出验证(退出码应为 0)
QT_QPA_PLATFORM=offscreen \
QT_QPA_PLATFORM_PLUGIN_PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/plugins/platforms \
PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/bin:$PATH \
./build_mw/docs/teaching/examples/qt_mainwindow_showcase.exe; echo "exit=$?"
```
## 教学要点小结
- 一个 `QMainWindow` = 菜单栏(≤1)+ 工具栏(多个)+ 状态栏(1)+ 浮动窗口(多个)+ 中心部件(1)。
- `QAction` 是「动作」的抽象,菜单与工具栏共享。
- 工具栏/浮动窗口都是**可移动**的,停靠区域可由 `setAllowedAreas` 限定。
- 资源文件(`.qrc`)把图标编译进可执行文件,运行时用 `:/...` 引用,无需关心磁盘文件。