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>
This commit is contained in:
张宗平
2026-07-07 22:59:05 +08:00
parent ea6f12e7ac
commit 05a685c952
45 changed files with 4599 additions and 6 deletions
@@ -0,0 +1,104 @@
# QMainWindow 教学综合示例(qt_mainwindow_showcase
覆盖川大 wiki「5 QMainWindow」(pageId 58954529https://wiki.suncaper.net/display/scu2023/5+QMainWindow)
**5.15.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 内**定义一次**,被菜单和工具栏**共同引用**
`<addaction name="..."/>`)——这正是 wiki 强调的「同一动作,加进菜单显示为菜单项、
加进工具栏显示为工具按钮」。例如 `actionNew` 同时出现在「文件」菜单和主工具栏。
### 2. 信号槽:on_\<objectName\>_\<signal\> 自动连接
槽命名遵循 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. 状态栏 API5.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.pnggen_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`)把图标编译进可执行文件,运行时用 `:/...` 引用,无需关心磁盘文件。
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""生成 qt_mainwindow_showcase 所需的极简 PNG 图标。
仅用 Python 标准库(zlib + struct)手写最小 PNG 编码器,无第三方依赖。
幂等:重复运行覆盖生成相同文件。
python gen_icons.py # *nix / 已配置 py 启动器的 Windows
py gen_icons.py # Windows 官方 py 启动器
"""
import os
import struct
import zlib
HERE = os.path.dirname(os.path.abspath(__file__))
ICONS_DIR = os.path.join(HERE, "icons")
# 5×7 点阵字模("1" = 前景白色像素)
GLYPHS = {
"M": ["10001", "11011", "10101", "10001", "10001", "10001", "10001"],
"N": ["10001", "11001", "10101", "10011", "10001", "10001", "10001"],
"Q": ["01110", "10001", "10001", "10001", "10101", "10011", "01111"],
"A": ["01110", "10001", "10001", "11111", "10001", "10001", "10001"],
"D": ["11110", "10001", "10001", "10001", "10001", "10001", "11110"],
}
def write_png(path, width, height, rgb_rows):
"""写一张 8 位、颜色类型 2RGB)的 PNG。rgb_rows: 每行 width*3 个 0-255 整数。"""
def chunk(tag, data):
body = tag + data
return (struct.pack(">I", len(data)) + body +
struct.pack(">I", zlib.crc32(body) & 0xffffffff))
sig = b"\x89PNG\r\n\x1a\n"
ihdr = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
raw = b"".join(b"\x00" + bytes(row) for row in rgb_rows) # 每行前加 filter byte(0)
idat = zlib.compress(raw, 9)
with open(path, "wb") as f:
f.write(sig + chunk(b"IHDR", ihdr) + chunk(b"IDAT", idat) + chunk(b"IEND", b""))
def make_icon(size, bg, letter):
"""生成 size×size 的像素矩阵:纯色背景 + 居中白色字母。"""
glyph = GLYPHS[letter]
bw, bh = 5, 7
pix = 1 if size <= 16 else 2 # 16px 用 1:1 字模,32px 放大 2 倍
gw, gh = bw * pix, bh * pix
ox = (size - gw) // 2
oy = (size - gh) // 2
rows = []
for y in range(size):
row = []
for x in range(size):
gx, gy = (x - ox) // pix, (y - oy) // pix
if 0 <= gx < bw and 0 <= gy < bh and glyph[gy][gx] == "1":
row.extend((255, 255, 255))
else:
row.extend(bg)
rows.append(row)
return rows
# (文件名, 尺寸, 背景色 RGB, 字母)
SPECS = [
("app.png", 32, (30, 80, 180), "M"),
("new.png", 16, (40, 150, 70), "N"),
("quit.png", 16, (200, 50, 50), "Q"),
("about.png", 16, (220, 140, 30), "A"),
("dock.png", 16, (130, 60, 180), "D"),
]
def main():
os.makedirs(ICONS_DIR, exist_ok=True)
for name, size, bg, letter in SPECS:
write_png(os.path.join(ICONS_DIR, name), size, size, make_icon(size, bg, letter))
print(f"wrote {name} ({size}x{size})")
print(f"done -> {ICONS_DIR}")
if __name__ == "__main__":
main()
Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

@@ -0,0 +1,22 @@
// ============================================================================
// mainwindow_showcase —— QMainWindow 教学综合示例(程序入口)
// 覆盖 wiki「5 QMainWindow」(pageId 58954529) 的 5.15.6 全部六大组件:
// 5.1 菜单栏 · 5.2 工具栏 · 5.3 状态栏 · 5.4 浮动窗口 · 5.5 中心部件 · 5.6 资源文件
// 形态:极简文本编辑器(QTextEdit 为中心部件),纯组件演示,无文件 I/O。
// ============================================================================
#include <QApplication>
#include <QTimer>
#include "mainwindow_showcase.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow w;
w.show();
// 仅在离屏自动化验证时(QT_QPA_PLATFORM=offscreen200ms 后自动退出;
// 正常运行(有显示环境)窗口保持打开,不自动退出。仿 p03/ch05 风格。
if (qgetenv("QT_QPA_PLATFORM") == "offscreen") {
QTimer::singleShot(200, &app, &QApplication::quit);
}
return app.exec();
}
@@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/">
<file>icons/app.png</file>
<file>icons/new.png</file>
<file>icons/quit.png</file>
<file>icons/about.png</file>
<file>icons/dock.png</file>
</qresource>
</RCC>
@@ -0,0 +1,299 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>QMainWindow 教学综合示例</string>
</property>
<property name="windowIcon">
<iconset resource="mainwindow.qrc">
<normaloff>:/icons/app.png</normaloff>:/icons/app.png</iconset>
</property>
<!-- 5.5 中心部件:QTextEdit(编辑器主体) -->
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="centralLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="textEdit">
<property name="plainText">
<string>欢迎使用 QMainWindow 教学综合示例。
在这里编辑文字,状态栏会实时显示光标行列与字数。
视图菜单可操控工具栏、状态栏与浮动窗口。</string>
</property>
</widget>
</item>
</layout>
</widget>
<!-- 5.1 菜单栏:文件 / 视图 / 帮助 -->
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>文件(&amp;F)</string>
</property>
<addaction name="actionNew"/>
<addaction name="actionQuit"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>视图(&amp;V)</string>
</property>
<widget class="QMenu" name="menuToolBarArea">
<property name="title">
<string>工具栏停靠区(&amp;A)</string>
</property>
<addaction name="actionToolBarAreaLeft"/>
<addaction name="actionToolBarAreaRight"/>
<addaction name="actionToolBarAreaTop"/>
<addaction name="actionToolBarAreaBottom"/>
<addaction name="actionToolBarAreaAll"/>
</widget>
<addaction name="actionToggleToolBar"/>
<addaction name="actionToggleStatusBar"/>
<addaction name="actionToggleToolBarMovable"/>
<addaction name="actionToggleDockFloating"/>
<addaction name="actionShowDock"/>
<addaction name="actionHideDock"/>
<addaction name="menuToolBarArea"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>帮助(&amp;H)</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuView"/>
<addaction name="menuHelp"/>
</widget>
<!-- 5.3 状态栏(.ui 占位,永久标签在 .cpp 用 addPermanentWidget 添加) -->
<widget class="QStatusBar" name="statusbar"/>
<!-- 5.2 工具栏:复用同一批 QAction(QAction 是菜单项与工具按钮的公共抽象) -->
<widget class="QToolBar" name="mainToolBar">
<property name="windowTitle">
<string>主工具栏</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionNew"/>
<addaction name="actionQuit"/>
<addaction name="actionToggleDockFloating"/>
<addaction name="actionAbout"/>
</widget>
<!-- 5.4 铆接部件(浮动窗口):可拖出浮动、停靠到边缘 -->
<widget class="QDockWidget" name="dockWidget">
<property name="windowTitle">
<string>浮动窗口</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockContent">
<layout class="QVBoxLayout" name="dockLayout">
<item>
<widget class="QLabel" name="dockLabel">
<property name="text">
<string>我是浮动窗口(QDockWidget5.4 铆接部件)。
可拖出浮动,也可停靠到主窗口边缘。
视图菜单的「停靠窗口浮动/停靠」可切换我的状态。</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<!-- 5.1 QAction:菜单项与工具按钮的公共抽象。图标取自 5.6 资源文件 -->
<action name="actionNew">
<property name="icon">
<iconset resource="mainwindow.qrc">
<normaloff>:/icons/new.png</normaloff>:/icons/new.png</iconset>
</property>
<property name="text">
<string>新建</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
<property name="statusTip">
<string>清空编辑区(5.5 中心部件)</string>
</property>
</action>
<action name="actionQuit">
<property name="icon">
<iconset resource="mainwindow.qrc">
<normaloff>:/icons/quit.png</normaloff>:/icons/quit.png</iconset>
</property>
<property name="text">
<string>退出</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
<property name="statusTip">
<string>退出程序</string>
</property>
</action>
<action name="actionToggleToolBar">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>显示主工具栏</string>
</property>
<property name="statusTip">
<string>显示或隐藏主工具栏(5.2 工具栏)</string>
</property>
</action>
<action name="actionToggleStatusBar">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>显示状态栏</string>
</property>
<property name="statusTip">
<string>显示或隐藏状态栏(5.3 状态栏)</string>
</property>
</action>
<action name="actionToggleToolBarMovable">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>工具栏可移动</string>
</property>
<property name="statusTip">
<string>切换工具栏是否可拖动(5.2 setMovable</string>
</property>
</action>
<action name="actionToggleDockFloating">
<property name="icon">
<iconset resource="mainwindow.qrc">
<normaloff>:/icons/dock.png</normaloff>:/icons/dock.png</iconset>
</property>
<property name="text">
<string>停靠窗口浮动/停靠</string>
</property>
<property name="statusTip">
<string>切换浮动窗口的浮动状态(5.4 setFloating</string>
</property>
</action>
<action name="actionShowDock">
<property name="text">
<string>显示浮动窗口</string>
</property>
<property name="statusTip">
<string>重新显示已关闭的浮动窗口(5.4 show</string>
</property>
</action>
<action name="actionHideDock">
<property name="text">
<string>关闭浮动窗口</string>
</property>
<property name="statusTip">
<string>隐藏浮动窗口(5.4 hide</string>
</property>
</action>
<action name="actionToolBarAreaLeft">
<property name="text">
<string>停靠左侧</string>
</property>
<property name="statusTip">
<string>限制工具栏只能停靠左侧(5.2 setAllowedAreas</string>
</property>
</action>
<action name="actionToolBarAreaRight">
<property name="text">
<string>停靠右侧</string>
</property>
<property name="statusTip">
<string>限制工具栏只能停靠右侧</string>
</property>
</action>
<action name="actionToolBarAreaTop">
<property name="text">
<string>停靠顶部</string>
</property>
<property name="statusTip">
<string>限制工具栏只能停靠顶部</string>
</property>
</action>
<action name="actionToolBarAreaBottom">
<property name="text">
<string>停靠底部</string>
</property>
<property name="statusTip">
<string>限制工具栏只能停靠底部</string>
</property>
</action>
<action name="actionToolBarAreaAll">
<property name="text">
<string>允许全部区域</string>
</property>
<property name="statusTip">
<string>工具栏允许停靠在任意区域</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset resource="mainwindow.qrc">
<normaloff>:/icons/about.png</normaloff>:/icons/about.png</iconset>
</property>
<property name="text">
<string>关于</string>
</property>
<property name="statusTip">
<string>关于本程序</string>
</property>
</action>
</widget>
<resources>
<include location="mainwindow.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,110 @@
// ============================================================================
// mainwindow_showcase.cpp —— MainWindow 实现(动态行为)
//
// 静态结构见 mainwindow.uiDesigner)。本文件只做:
// - 资源文件应用(5.6):setWindowIcon 从 .qrc 加载
// - 状态栏(5.3):addPermanentWidget + 实时行列/字数 + showMessage 临时提示
// - 信号槽:on_<objectName>_<signal> 自动连接(connectSlotsByName
// - 视图菜单:运行时操控工具栏/状态栏/浮动窗口
// ============================================================================
#include "mainwindow_showcase.h"
#include "ui_mainwindow.h" // AUTOUIC 从 mainwindow.ui 生成
#include <QAction>
#include <QDockWidget>
#include <QIcon>
#include <QLabel>
#include <QMessageBox>
#include <QStatusBar>
#include <QTextCursor>
#include <QTextEdit>
#include <QToolBar>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
// 5.6 资源文件:应用图标从 mainwindow.qrc 加载(:/icons/app.png
setWindowIcon(QIcon(":/icons/app.png"));
// 5.3 状态栏:永久标签(addPermanentWidget),实时显示光标行列与字数
statusPosLabel = new QLabel(QStringLiteral("行:1 列:1 字数:0"), this);
statusBar()->addPermanentWidget(statusPosLabel);
// 5.5 中心部件 QTextEdit 的内容/光标变化 → 更新状态栏
connect(ui->textEdit, &QTextEdit::textChanged, this, &MainWindow::updateStatusBar);
connect(ui->textEdit, &QTextEdit::cursorPositionChanged, this, &MainWindow::updateStatusBar);
updateStatusBar();
}
MainWindow::~MainWindow() { delete ui; }
// ---- 文件菜单 ----------------------------------------------------------
void MainWindow::on_actionNew_triggered() {
ui->textEdit->clear();
statusBar()->showMessage(QStringLiteral("已新建"), 2000); // 5.3 临时提示
}
void MainWindow::on_actionQuit_triggered() { close(); }
// ---- 视图菜单 —— 运行时操控各组件 --------------------------------------
void MainWindow::on_actionToggleToolBar_toggled(bool checked) {
ui->mainToolBar->setVisible(checked);
}
void MainWindow::on_actionToggleStatusBar_toggled(bool checked) {
statusBar()->setVisible(checked);
}
void MainWindow::on_actionToggleToolBarMovable_toggled(bool checked) {
ui->mainToolBar->setMovable(checked); // 5.2 setMovable
}
void MainWindow::on_actionToggleDockFloating_triggered() {
ui->dockWidget->setFloating(!ui->dockWidget->isFloating()); // 5.4 setFloating
}
void MainWindow::on_actionShowDock_triggered() {
ui->dockWidget->show(); // 5.4 重新显示已关闭的浮动窗口
}
void MainWindow::on_actionHideDock_triggered() {
ui->dockWidget->hide(); // 5.4 关闭(隐藏)浮动窗口
}
void MainWindow::on_actionToolBarAreaLeft_triggered() {
ui->mainToolBar->setAllowedAreas(Qt::LeftToolBarArea); // 5.2 setAllowedAreas
}
void MainWindow::on_actionToolBarAreaRight_triggered() {
ui->mainToolBar->setAllowedAreas(Qt::RightToolBarArea);
}
void MainWindow::on_actionToolBarAreaTop_triggered() {
ui->mainToolBar->setAllowedAreas(Qt::TopToolBarArea);
}
void MainWindow::on_actionToolBarAreaBottom_triggered() {
ui->mainToolBar->setAllowedAreas(Qt::BottomToolBarArea);
}
void MainWindow::on_actionToolBarAreaAll_triggered() {
ui->mainToolBar->setAllowedAreas(Qt::AllToolBarAreas);
}
// ---- 帮助菜单 ----------------------------------------------------------
void MainWindow::on_actionAbout_triggered() {
QMessageBox::about(this, QStringLiteral("关于"),
QStringLiteral(
"<b>QMainWindow 教学综合示例</b><br><br>"
"一个极简文本编辑器,演示 QMainWindow 的六大核心组件"
"wiki「5 QMainWindow」pageId 58954529):<br>"
"5.1 菜单栏 · 5.2 工具栏 · 5.3 状态栏 · "
"5.4 浮动窗口 · 5.5 中心部件 · 5.6 资源文件"));
}
// ---- 状态栏更新 --------------------------------------------------------
void MainWindow::updateStatusBar() {
const QTextCursor c = ui->textEdit->textCursor();
const int line = c.blockNumber() + 1;
const int col = c.columnNumber() + 1;
const int count = ui->textEdit->toPlainText().size();
statusPosLabel->setText(
QStringLiteral("行:%1 列:%2 字数:%3").arg(line).arg(col).arg(count));
}
@@ -0,0 +1,57 @@
// ============================================================================
// mainwindow_showcase.h —— MainWindow 类声明
//
// 主窗体的静态结构(central QTextEdit / menuBar / mainToolBar / statusBar /
// dockWidget / 全部 QAction)在 mainwindow.ui 中用 Qt Designer 设计;
// 本类只负责动态行为:信号槽、运行时操控、状态栏更新、关于对话框。
//
// 槽命名遵循 Qt 自动连接约定 on_<objectName>_<signal>,由 setupUi() 触发的
// QMetaObject::connectSlotsByName 自动连接,无需手写 connectKISS)。
// ============================================================================
#ifndef MAINWINDOW_SHOWCASE_H
#define MAINWINDOW_SHOWCASE_H
#include <QMainWindow>
#include <QLabel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
private slots:
// 文件菜单
void on_actionNew_triggered();
void on_actionQuit_triggered();
// 视图菜单 —— 操控各组件
void on_actionToggleToolBar_toggled(bool checked); // 5.2 显示/隐藏工具栏
void on_actionToggleStatusBar_toggled(bool checked); // 5.3 显示/隐藏状态栏
void on_actionToggleToolBarMovable_toggled(bool checked); // 5.2 工具栏可移动
void on_actionToggleDockFloating_triggered(); // 5.4 浮动/停靠切换
void on_actionShowDock_triggered(); // 5.4 重新显示浮动窗口
void on_actionHideDock_triggered(); // 5.4 关闭浮动窗口
void on_actionToolBarAreaLeft_triggered(); // 5.2 setAllowedAreas
void on_actionToolBarAreaRight_triggered();
void on_actionToolBarAreaTop_triggered();
void on_actionToolBarAreaBottom_triggered();
void on_actionToolBarAreaAll_triggered();
// 帮助菜单
void on_actionAbout_triggered();
// 状态栏:根据中心编辑区更新「行:列 字数:N」
void updateStatusBar();
private:
Ui::MainWindow *ui;
QLabel *statusPosLabel; // 状态栏永久标签(演示 5.3 addPermanentWidget
};
#endif // MAINWINDOW_SHOWCASE_H