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:
@@ -0,0 +1,104 @@
|
||||
# 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 内**定义一次**,被菜单和工具栏**共同引用**
|
||||
(`<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. 状态栏 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`)把图标编译进可执行文件,运行时用 `:/...` 引用,无需关心磁盘文件。
|
||||
@@ -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 位、颜色类型 2(RGB)的 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.1–5.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=offscreen)200ms 后自动退出;
|
||||
// 正常运行(有显示环境)窗口保持打开,不自动退出。仿 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>文件(&F)</string>
|
||||
</property>
|
||||
<addaction name="actionNew"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>视图(&V)</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuToolBarArea">
|
||||
<property name="title">
|
||||
<string>工具栏停靠区(&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>帮助(&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>我是浮动窗口(QDockWidget,5.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.ui(Designer)。本文件只做:
|
||||
// - 资源文件应用(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 自动连接,无需手写 connect(KISS)。
|
||||
// ============================================================================
|
||||
#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
|
||||
Reference in New Issue
Block a user