PySide6从0开始学习的笔记(四)QMainWindow

前面几节,学习了pyside6的基本代码框架以及容器控件和布局工具,这一节,学习QMainWindow。

QMainWindow 是 Qt 框架中用于创建主窗口应用程序的核心类,专门设计用于构建具有标准桌面应用结构的窗口,是 Qt 桌面开发中最常用的顶层窗口类之一。


简单介绍:

  1. 固定布局结构(Qt 内置,无需手动拼接):

    • 标题栏(默认包含窗口控制按钮:最小化、最大化、关闭);
    • 中心部件(central widget):窗口的核心内容区域(必须通过 setCentralWidget() 设置,否则中心区域为空);
    • 可选组件:菜单栏(menu bar)、工具栏(tool bar)、状态栏(status bar)、停靠窗口(dock widget,可拖拽停靠的辅助窗口)。
  2. 核心功能

    • 支持菜单 / 工具栏 / 状态栏的快速创建与管理(如 menuBar() 获取菜单栏、statusBar() 显示状态栏提示);
    • 支持停靠窗口(QDockWidget)的添加与布局管理(可停靠在窗口四周或浮动);
    • 内置窗口状态管理(最大化、最小化、还原、窗口大小调整);
    • 支持 Qt 样式表(QSS)美化,适配不同桌面风格。
  3. 使用场景

    • 复杂桌面应用(如文本编辑器、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 桌面应用的核心容器,其优势在于标准化的窗口结构和丰富的内置功能。

使用时需重点关注:

  1. 必须设置中央部件;
  2. 合理使用菜单栏 / 工具栏 / 状态栏提升用户体验;
  3. 注意内存管理(移除部件时手动释放);
  4. 使用setWindowFlags()的方式实现风格多样的定制窗口;
  5. 菜单系统的核心是Qaction,通过连接(connect)的方式将菜单的点击动作与槽函数的响应连接。QAction 并非直接的 "菜单按钮" 控件,而是对 "用户可执行动作" 的抽象封装 ------ 比如 "打开文件""复制""退出" 这些功能,都会先封装成 QAction 对象。它不仅包含动作的文本(如 "打开")、图标、快捷键,还内置了 "是否可用""是否勾选" 等状态,是菜单、工具栏、快捷键等功能的 "功能核心"(同一 QAction 可同时关联菜单和工具栏,状态同步)。
  6. **QAction的连接机制:通过connect()连接信号与槽,**Qt 的核心通信机制是 "信号与槽",所以,菜单点击的本质是:当用户点击菜单中对应的选项时,该选项关联的 QAction 会发射一个预设信号(如 triggered() 触发信号);通过 connect() 函数,将这个信号 "绑定" 到一个自定义的 "槽函数"(即需要执行的功能代码,如 onOpenFile())。
  7. QAction的 响应逻辑:点击 → 信号 → 槽函数完整流程:用户点击菜单选项 → 关联的 QAction 发射 triggered() 信号 → connect() 建立的关联生效 → 触发绑定的槽函数执行 → 实现具体功能(如弹出文件选择框、退出程序)。

下一节,将要学习QT的最重要也是最精彩的概念:信号与槽。

相关推荐
sheeta19988 小时前
LeetCode 每日一题笔记 日期:2025.12.15 题目:2110.股票平滑下跌阶段的数目
笔记·算法·leetcode
深蓝海拓8 小时前
PySide6 的 QSettings简单应用学习笔记
python·学习·pyqt
码界奇点15 小时前
Python从0到100一站式学习路线图与实战指南
开发语言·python·学习·青少年编程·贴图
智者知已应修善业15 小时前
【求中位数】2024-1-23
c语言·c++·经验分享·笔记·算法
张人玉15 小时前
百度 AI 图像识别 WinForms 应用代码分析笔记
人工智能·笔记·百度
Laravel技术社区16 小时前
pytesseract 中英文 识别图片文字
python
xqqxqxxq16 小时前
背单词软件技术笔记(V1.0核心版及V2.0随机挖字母)
笔记
YJlio16 小时前
Active Directory 工具学习笔记(10.8):AdInsight——保存与导出(证据留存、共享与二次分析)
数据库·笔记·学习
生骨大头菜17 小时前
使用python实现相似图片搜索功能,并接入springcloud
开发语言·python·spring cloud·微服务