前面几节,学习了pyside6的基本代码框架以及容器控件和布局工具,这一节,学习QMainWindow。
QMainWindow 是 Qt 框架中用于创建主窗口应用程序的核心类,专门设计用于构建具有标准桌面应用结构的窗口,是 Qt 桌面开发中最常用的顶层窗口类之一。
简单介绍:
-
固定布局结构(Qt 内置,无需手动拼接):
- 标题栏(默认包含窗口控制按钮:最小化、最大化、关闭);
- 中心部件(central widget):窗口的核心内容区域(必须通过
setCentralWidget()设置,否则中心区域为空); - 可选组件:菜单栏(menu bar)、工具栏(tool bar)、状态栏(status bar)、停靠窗口(dock widget,可拖拽停靠的辅助窗口)。
-
核心功能:
- 支持菜单 / 工具栏 / 状态栏的快速创建与管理(如
menuBar()获取菜单栏、statusBar()显示状态栏提示); - 支持停靠窗口(QDockWidget)的添加与布局管理(可停靠在窗口四周或浮动);
- 内置窗口状态管理(最大化、最小化、还原、窗口大小调整);
- 支持 Qt 样式表(QSS)美化,适配不同桌面风格。
- 支持菜单 / 工具栏 / 状态栏的快速创建与管理(如
-
使用场景:
- 复杂桌面应用(如文本编辑器、IDE、播放器、管理工具等),需要菜单、工具栏、状态栏配合核心内容的场景;
- 简单窗口(仅需单一内容区域)也可使用,但更轻量的选择是 QWidget。
QMainWindow详解:
一、QMainWindow 核心结构
QMainWindow 预设了标准化的窗口布局,核心组成部分如下:
| 组件 | 说明 |
|---|---|
| 菜单栏 (MenuBar) | 窗口顶部的菜单(如 File/Edit/Help),支持多级子菜单、快捷键、分隔符 |
| 工具栏 (ToolBar) | 可拖拽的快捷操作栏,包含按钮、图标等,支持停靠 / 浮动、显示 / 隐藏 |
| 中央部件 (Central Widget) | 窗口核心区域,必须设置(否则窗口可能显示异常),可放任意 Widget(如 QWidget/QTextEdit) |
| 状态栏 (StatusBar) | 窗口底部的状态提示栏,支持临时消息、永久部件、进度条等 |
| 停靠窗口 (DockWidget) | 可停靠在主窗口四周的浮动面板(如 IDE 的侧边栏),支持拖拽、关闭、浮动 |
| 标题栏 | 包含窗口标题、最小化 / 最大化 / 关闭按钮(可自定义) |
二、基础使用步骤
1. 最小化示例(创建空主窗口)
python
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 1. 基础窗口设置
self.setWindowTitle("QMainWindow 基础示例") # 设置窗口标题
self.resize(800, 600) # 设置窗口大小
self.setMinimumSize(400, 300) # 设置最小尺寸
self.setMaximumSize(1200, 900) # 设置最大尺寸
# 2. 设置中央部件(必须!否则窗口无内容)
central_widget = QWidget()
self.setCentralWidget(central_widget)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show() # 显示窗口
sys.exit(app.exec())

2. 核心组件完整示例
以下示例包含菜单栏、工具栏、状态栏、中央部件、停靠窗口的完整实现:
python
import sys
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout,
QMenuBar, QMenu, QToolBar, QStatusBar,
QTextEdit, QDockWidget, QLabel, QPushButton, QStyle
)
from PySide6.QtGui import QIcon, QKeySequence, QAction
from PySide6.QtCore import Qt
print([attr for attr in dir(QStyle.StandardPixmap) if not attr.startswith('_')])
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
# ========== 1. 基础窗口配置 ==========
self.setWindowTitle("QMainWindow 完整示例") # 窗口标题
self.resize(1000, 700) # 窗口大小
self.center_window() # 窗口居中
# ========== 2. 中央部件(核心区域) ==========
self.central_edit = QTextEdit() # 创建一个文本编辑区
self.central_edit.setPlaceholderText("这是中央部件(文本编辑区)") # 预设的占位字符
self.setCentralWidget(self.central_edit) # 将一个文本编辑区设为QMainWindow的中央部件
# ========== 3. 菜单栏 (MenuBar) ==========
self.init_menubar() # 初始化菜单栏
# ========== 4. 工具栏 (ToolBar) ==========
self.init_toolbar() # 初始化工具栏
# ========== 5. 状态栏 (StatusBar) ==========
self.init_statusbar() # 初始化状态栏
# ========== 6. 停靠窗口 (DockWidget) ==========
self.init_dockwidget() # 初始化停靠窗口
def center_window(self):
"""将本窗口在显示器屏幕居中显示"""
screen_geo = QApplication.primaryScreen().geometry() # 获取屏幕尺寸
window_geo = self.frameGeometry() # 获取窗口尺寸
window_geo.moveCenter(screen_geo.center()) # 将窗口中心移动到屏幕中心
self.move(window_geo.topLeft()) # 将窗口中心移动到屏幕中心
def init_menubar(self):
"""初始化菜单栏"""
# 获取默认菜单栏(QMainWindow自带,无需手动创建)
menubar = self.menuBar() # menuBar()是QT内置的默认菜单栏
# 1. "文件"菜单
file_menu = menubar.addMenu("文件(&F)") # 在菜单中新增一个主菜单项"文件",&F:支持Alt+F快捷键
# 新建动作
self.new_action = QAction(QIcon("new.ico"), "新建(&N)", self) # 创建一个动作(QAction),QIcon():图标。常用图标可以使用HA_IconsExtract等类似软件从system32\shell32.dll中以及其他dll中提取
self.new_action.setShortcut(QKeySequence.New) # 为当前动作绑定标准快捷键,QKeySequence.New:Qt 预定义的 标准快捷键常量,代表 "新建" 操作的系统默认快捷键(跨平台自适应)。
# 关键作用:给 "新建" 相关的动作(比如 "文件→新建" 菜单)绑定系统统一的快捷键,无需手动写 Ctrl+N(Windows/Linux)或 Cmd+N(Mac)------ Qt 会自动根据操作系统适配对应的快捷键,保证跨平台一致性。
self.new_action.triggered.connect(lambda: self.central_edit.clear()) # 绑定动作触发事件(点击后执行的操作)
file_menu.addAction(self.new_action) # 将新建动作添加到主菜单项"文件"中
# 打开动作
self.open_action = QAction(QIcon("open.ico"), "打开(&O)", self) # 创建一个动作(QAction)
self.open_action.setShortcut(QKeySequence.Open) # 为当前动作绑定自定义快捷键(跨平台自适应)
self.open_action.triggered.connect(lambda: self.status_bar.showMessage("打开文件...", 2000)) # 绑定动作触发事件(点击后执行的操作)
file_menu.addAction(self.open_action) # 将打开动作添加到主菜单项"文件"中
# 分隔符
file_menu.addSeparator()
# 退出动作
exit_action = QAction("退出(&E)", self) # 创建一个动作(QAction)
exit_action.setShortcut(QKeySequence.Quit) # 为当前动作绑定自定义快捷键(跨平台自适应)
exit_action.triggered.connect(QApplication.quit) # 绑定动作触发事件(点击后执行的操作)
file_menu.addAction(exit_action)
# 2. "编辑"菜单
edit_menu = menubar.addMenu("编辑(&E)") # 在菜单中新增一个主菜单项"编辑",&E:支持Alt+E快捷键
undo_action = QAction(QIcon("undo.ico"), "撤销(&U)", self) # 创建一个动作(QAction)
undo_action.setShortcut(QKeySequence.Undo) # 为当前动作绑定自定义快捷键(跨平台自适应)
undo_action.triggered.connect(self.central_edit.undo) # 绑定动作触发事件(点击后执行的操作)
edit_menu.addAction(undo_action) # 将撤销动作添加到主菜单项"编辑"中
def init_toolbar(self):
"""初始化工具栏"""
# 创建工具栏(可指定标题,支持多工具栏)
toolbar = QToolBar("主工具栏", self)
self.addToolBar(toolbar) # 添加到主窗口
# 设置工具栏属性
toolbar.setMovable(True) # 允许拖拽停靠
toolbar.setFloatable(True) # 允许浮动
toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # 文字在图标旁
# 添加动作(可复用菜单栏的动作)
toolbar.addAction(self.new_action) # 将菜单栏的"新建"动作添加到工具栏
toolbar.addSeparator()
toolbar.addAction(self.open_action) # 将菜单栏的"打开"动作添加到工具栏
# 自定义工具栏按钮
custom_btn = QPushButton("工具栏按钮")
custom_btn.clicked.connect(lambda: self.central_edit.append("工具栏按钮被点击!"))
toolbar.addWidget(custom_btn) # 工具栏可添加任意Widget
def init_statusbar(self):
"""初始化状态栏"""
# 获取默认状态栏(QMainWindow自带)
self.status_bar = self.statusBar()
self.status_bar.setSizeGripEnabled(True) # 右下角调整大小的手柄
# 永久显示的部件
status_label = QLabel("就绪")
self.status_bar.addPermanentWidget(status_label) # 添加到永久显示的部件
# 临时消息(默认5秒消失,可指定毫秒)
self.status_bar.showMessage("主窗口已加载完成", 3000)
def init_dockwidget(self):
"""初始化停靠窗口"""
# 创建停靠窗口
dock = QDockWidget("侧边面板", self) # QDockWidget是QMainWindow自带的停靠窗口
dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) # 仅允许左右停靠
dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) # 可移动、可关闭
# 停靠窗口的内容
dock_widget = QWidget() # 创建一个Widget
dock_layout = QVBoxLayout(dock_widget) # 创建一个垂直布局(QVBoxLayout)
dock_layout.addWidget(QLabel("这是停靠窗口内容")) # 添加一个标签
dock_btn = QPushButton("停靠窗口按钮") # 添加一个按钮
dock_btn.clicked.connect(lambda: self.central_edit.append("停靠窗口按钮点击!")) # 绑定按钮点击事件
dock_layout.addWidget(dock_btn) # 添加按钮
dock_layout.addStretch() # 添加一个伸缩因子,使内容垂直居中
dock.setWidget(dock_widget)
self.addDockWidget(Qt.LeftDockWidgetArea, dock) # 添加到左侧
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())


三、关键 API 详解
1. 窗口基础设置
| 方法 | 说明 |
|---|---|
setWindowTitle(str) |
设置窗口标题 |
resize(w, h) |
设置窗口大小 |
setMinimumSize(w, h) |
设置最小尺寸 |
setMaximumSize(w, h) |
设置最大尺寸 |
move(x, y) |
移动窗口到指定坐标 |
center_window()(自定义) |
窗口居中(需自己实现,如示例) |
setWindowIcon(QIcon) |
设置窗口图标 |
setWindowState(Qt.WindowMaximized) |
最大化窗口(Qt.WindowMinimized/FullScreen) |
2. 中央部件
| 方法 | 说明 |
|---|---|
setCentralWidget(QWidget) |
设置中央部件(必须调用) |
centralWidget() |
获取当前中央部件 |
takeCentralWidget() |
移除并返回中央部件(需手动删除避免内存泄漏) |
3. 菜单栏
| 方法 | 说明 |
|---|---|
menuBar() |
获取默认菜单栏(QMainWindow 自动创建) |
addMenu(str/QMenu) |
添加菜单(支持 & 符号设置快捷键,如 & File) |
QAction |
菜单动作(设置文本、图标、快捷键、触发事件) |
action.triggered.connect(func) |
绑定动作触发事件 |
4. 工具栏
| 方法 | 说明 |
|---|---|
addToolBar(QToolBar/str) |
添加工具栏(可创建多个) |
addToolBarBreak() |
工具栏换行 |
setToolBarArea(Qt.ToolBarArea) |
设置工具栏默认停靠区域(Top/Bottom/Left/Right) |
QToolBar.setMovable(bool) |
是否允许拖拽 |
QToolBar.addWidget(QWidget) |
添加自定义部件(如按钮、输入框) |
5. 状态栏
| 方法 | 说明 |
|---|---|
statusBar() |
获取默认状态栏 |
showMessage(str, int=0) |
显示临时消息(毫秒数,0 表示一直显示) |
clearMessage() |
清除临时消息 |
addPermanentWidget(QWidget) |
添加永久显示的部件(如状态标签) |
removeWidget(QWidget) |
移除永久部件 |
6. 停靠窗口
| 方法 | 说明 |
|---|---|
addDockWidget(Qt.DockWidgetArea, QDockWidget) |
添加停靠窗口到指定区域 |
QDockWidget.setAllowedAreas(Qt.DockWidgetArea) |
设置允许停靠的区域 |
QDockWidget.setFeatures(QDockWidget.Feature) |
设置停靠窗口特性(可移动 / 关闭 / 浮动等) |
tabifyDockWidget(dock1, dock2) |
将多个停靠窗口合并为标签页 |
四、常见问题与注意事项
1. 中央部件必须设置
如果未调用 setCentralWidget(),QMainWindow 的布局会异常(窗口可能无法正常显示内容),即使只是空的 QWidget 也需要设置。
2. 内存泄漏问题
- 移除中央部件 / 工具栏 / 停靠窗口时,需手动删除部件(takeCentralWidget() 仅移除,不释放内存):
python
old_central = self.takeCentralWidget()
if old_central:
old_central.deleteLater() # 安全释放内存
3. 菜单栏 / 状态栏的默认创建
QMainWindow 会自动创建菜单栏和状态栏(调用 menuBar()/statusBar() 时初始化),无需手动 new QMenuBar()。
4. 快捷键冲突
- 使用 QKeySequence 标准快捷键(如 QKeySequence.New)而非硬编码,适配不同系统(Mac 为 Cmd,Windows 为 Ctrl)。
- 避免自定义快捷键与系统冲突(如 Ctrl+Alt+Del)。
5. 工具栏样式
通过 setToolButtonStyle() 可调整工具栏按钮样式:
- Qt.ToolButtonIconOnly:仅显示图标
- Qt.ToolButtonTextOnly:仅显示文字
- Qt.ToolButtonTextBesideIcon:文字在图标旁
- Qt.ToolButtonTextUnderIcon:文字在图标下
五、进阶用法
**1.**隐藏默认标题栏
隐藏默认标题栏,获得一个固定于屏幕的无标题栏、无最大化最小化和关闭按钮的也无法拖拽的窗口,比如很多软件的用户名输入界面。
python
self.setWindowFlags(Qt.FramelessWindowHint) # 隐藏默认标题栏,如需拖拽,需重写 mousePressEvent/mouseMoveEvent 实现拖拽:
除了使用Qt.FramelessWindowHint 隐藏默认标题栏,还可以通过setWindowFlags()的方式实现更多定制的窗口。
python
self.setWindowFlag(Qt.WindowStaysOnTopHint) # 可以单独设置窗口是否置顶
self.setWindowFlag(Qt.WindowMinimizeButtonHint) # 可以单独设置窗口是否显示最小化按钮
self.setWindowFlag(Qt.WindowMaximizeButtonHint) # 可以单独设置窗口是否显示最大化按钮
self.setWindowFlag(Qt.WindowCloseButtonHint) # 可以单独设置窗口是否显示关闭按钮
self.setWindowFlag(Qt.WindowContextHelpButtonHint) # 可以单独设置窗口是否显示帮助按钮
self.setWindowFlag(Qt.WindowFullscreenButtonHint) # 可以单独设置窗口是否显示全屏按钮
self.setWindowFlag(Qt.WindowSystemMenuHint) # 可以单独设置窗口是否显示系统菜单
self.setWindowFlag(Qt.WindowTitleHint) # 可以单独设置窗口是否显示标题栏
2. 多文档界面(MDI)
结合 QMdiArea 作为中央部件,实现多文档窗口:
python
import sys
from PySide6.QtWidgets import (
QApplication, QMainWindow, QTextEdit, QStyle, QMdiArea, QMdiSubWindow
)
print([attr for attr in dir(QStyle.StandardPixmap) if not attr.startswith('_')])
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.resize(600,400)
mdi_area = QMdiArea() # 创建多文档区域
self.setCentralWidget(mdi_area) # 将多文档区域设为中心控件
# 添加子窗口
sub_window1 = QMdiSubWindow()
sub_window1.setWidget(QTextEdit())
sub_window1.setWindowTitle("子窗口1")
sub_window2 = QMdiSubWindow()
sub_window2.setWidget(QTextEdit())
sub_window2.setWindowTitle("子窗口2")
mdi_area.addSubWindow(sub_window1)
mdi_area.addSubWindow(sub_window2)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())

3. 保存 / 恢复窗口状态
保存窗口大小、位置、工具栏 / 停靠窗口布局:
python
import sys
from PySide6.QtWidgets import (
QApplication, QMainWindow, QTextEdit, QDockWidget,
QLabel, QWidget, QVBoxLayout
)
from PySide6.QtCore import (
QSettings, QByteArray, Qt, QCoreApplication
)
# 全局配置:统一组织名/应用名(必须和保存时一致)
ORG_NAME = "MyCompany"
APP_NAME = "DockRestoreApp"
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 1. 先初始化所有UI部件(包括停靠窗口、工具栏)
self.init_base_ui()
self.init_dock_widgets()
# 2. 后恢复窗口状态(关键顺序)
self.restore_window_state()
def init_base_ui(self):
"""初始化基础UI(中央部件、窗口属性)"""
self.setWindowTitle("停靠窗口状态恢复示例")
# 中央部件(必须设置)
central_widget = QTextEdit()
central_widget.setPlaceholderText("中央编辑区")
self.setCentralWidget(central_widget)
def init_dock_widgets(self):
"""初始化停靠窗口(必须设置唯一ObjectName)"""
# 左侧停靠窗口(核心:设置唯一ObjectName,Qt靠这个识别停靠窗口)
left_dock = QDockWidget("左侧面板", self)
left_dock.setObjectName("LeftDockWidget") # 必须!
# 停靠窗口内容
dock_content = QWidget()
dock_layout = QVBoxLayout(dock_content)
dock_layout.addWidget(QLabel("左侧停靠窗口内容"))
dock_layout.addStretch()
left_dock.setWidget(dock_content)
# 设置停靠规则
left_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
left_dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
# 添加到主窗口
self.addDockWidget(Qt.LeftDockWidgetArea, left_dock)
# 可选:添加第二个停靠窗口测试
right_dock = QDockWidget("右侧面板", self)
right_dock.setObjectName("RightDockWidget") # 必须!
right_dock.setWidget(QLabel("右侧停靠窗口内容"))
self.addDockWidget(Qt.RightDockWidgetArea, right_dock)
def restore_window_state(self):
"""恢复窗口状态(几何+布局)"""
# 初始化QSettings(强制INI格式,方便调试,可选)
QSettings.setDefaultFormat(QSettings.IniFormat)
settings = QSettings(ORG_NAME, APP_NAME)
# 1. 恢复窗口几何状态(位置、大小)
geometry = settings.value("geometry", QByteArray())
if isinstance(geometry, str):
geometry = QByteArray.fromBase64(geometry.encode())
if not geometry.isEmpty():
# 恢复失败则用默认大小
if not self.restoreGeometry(geometry):
self.resize(1000, 700)
# 2. 恢复窗口布局状态(核心:停靠窗口/工具栏)
window_state = settings.value("windowState", QByteArray())
if isinstance(window_state, str):
window_state = QByteArray.fromBase64(window_state.encode())
if not window_state.isEmpty():
# 关键:指定版本号(默认0,若布局变更可升级版本)
self.restoreState(window_state, version=0)
def closeEvent(self, event):
"""窗口关闭时保存状态"""
settings = QSettings(ORG_NAME, APP_NAME)
# 保存几何状态
settings.setValue("geometry", self.saveGeometry())
# 保存布局状态(包含停靠窗口)
settings.setValue("windowState", self.saveState(version=0))
# 立即同步到磁盘(避免延迟)
settings.sync()
# 允许关闭
event.accept()
if __name__ == "__main__":
# 全局设置组织名/应用名(QSettings依赖)
QCoreApplication.setOrganizationName(ORG_NAME)
QCoreApplication.setApplicationName(APP_NAME)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
进一步的QSettings学习见:https://blog.csdn.net/xulibo5828/article/details/155953699
总结:
QMainWindow 是 PySide6 桌面应用的核心容器,其优势在于标准化的窗口结构和丰富的内置功能。
使用时需重点关注:
- 必须设置中央部件;
- 合理使用菜单栏 / 工具栏 / 状态栏提升用户体验;
- 注意内存管理(移除部件时手动释放);
- 使用setWindowFlags()的方式实现风格多样的定制窗口;
- 菜单系统的核心是Qaction,通过连接(connect)的方式将菜单的点击动作与槽函数的响应连接。QAction 并非直接的 "菜单按钮" 控件,而是对 "用户可执行动作" 的抽象封装 ------ 比如 "打开文件""复制""退出" 这些功能,都会先封装成 QAction 对象。它不仅包含动作的文本(如 "打开")、图标、快捷键,还内置了 "是否可用""是否勾选" 等状态,是菜单、工具栏、快捷键等功能的 "功能核心"(同一 QAction 可同时关联菜单和工具栏,状态同步)。
- **QAction的连接机制:通过connect()连接信号与槽,**Qt 的核心通信机制是 "信号与槽",所以,菜单点击的本质是:当用户点击菜单中对应的选项时,该选项关联的 QAction 会发射一个预设信号(如 triggered() 触发信号);通过 connect() 函数,将这个信号 "绑定" 到一个自定义的 "槽函数"(即需要执行的功能代码,如 onOpenFile())。
- QAction的 响应逻辑:点击 → 信号 → 槽函数完整流程:用户点击菜单选项 → 关联的 QAction 发射 triggered() 信号 → connect() 建立的关联生效 → 触发绑定的槽函数执行 → 实现具体功能(如弹出文件选择框、退出程序)。
下一节,将要学习QT的最重要也是最精彩的概念:信号与槽。
