24. 多文档窗口控件

一、什么是MDI窗口

  MDI 窗口(Multiple-Document Interface),又称多文档界面,它主要用于同时显示多个文档,每个文档显示在各自的窗口中。MDI 窗口中通常包含子菜单和窗口菜单,用于在窗口或文档之间进行切换。

  用 QMainWindow 建立的主界面,通常会同时建立或打开多个相互独立的文档,这些文档共享主界面的菜单、工具栏和停靠控件,多文档中只有一个文档是活跃的文档,菜单和工具栏的操作只针对当前活跃的文档。主界面要实现多文档操作需要用 QMdiArea 控件,通常把 QMdiArea 定义成中心控件。可以在 QMdiArea 控件中添加多个子窗口 QMdiSubWindow,通常每个子窗口都有相同的控件,当然控件也可以不相同。

  我们可以在终端中使用 pip 安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载

pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple

  国内常用的 pip 下载源列表:

二、多文档区

  多文档区 QMdiArea 和子窗口 QMdiSubWindow 的继承关系如下所示:

QMdiArea与QMdiSubWindow的继承关系

  用 QMdiArea 类和 QMdiSubWindow 类创建多文档实例和子窗口实例的方法如下:

QMdiArea(parent:QWidget=None)
QMdiSubWindow(parent:QWidget=None, flags:Qt.WindowFlags=Default(Qt.WindowFlags))

  QMidArea 类常用的方法及其说明如下:

# 实例方法
addSubWindow(widget:QWidget, flags:Qt.WindowFlags=Qt.WindowFlags()) -> QMdiSubWindow    # 添加子窗口
removeSubWindow(widget:QWidget) -> None                                         # 删除子窗口

setViewMode(mode:QMdiArea.ViewMode) -> None                                     # 设置MDI区域的视图模式
viewMode() -> QMdiArea.ViewMode                                                 # 获取MDI区域的视图模式

currentSubWindow() -> QMdiSubWindow                                             # 获取当前子窗口

scrollContentsBy(dx:int, dy:int) -> None                                        # 移动子窗口中的控件
setActivationOrder(order:QMdiArea.WindowOrder) -> None                          # 设置子窗口的活跃顺序
activationOrder() -> QMdiArea.WindowOrder                                       # 获取子窗口的活跃顺序
subWindowList(order:QMdiArea.WindowOrder=QMdiArea.WindowOrder.CreationOrder) -> List[QMdiSubWindow]     # 按照指定顺序获取子窗口列表
activeSubWindow() -> QMdiSubWindow                                              # 获取活跃的子窗口

setBackground(background:Union[QBrush, QColor, Qt.GlobalColor, QGradient]) -> None  # 设置背景,默认是灰色
background() -> QBrush                                                          # 获取背景色画刷

setOption(option:QMdiArea.Option, on:bool=True) -> None                         # 设置子窗口选项
testOption(opton:QMdiArea.Option) -> bool                                       # 获取子窗口是否设置了选项

setTabPosition(position:QTabWidget.TabPosition) -> None                         # 设置Tab标签的位置
tabPosition() -> QTabWidget.TabPosition                                         # 获取Tab标签的位置

setTabShape(shape:QTabWidget.TabShape) -> None                                  # 设置Tab标签的形状
tabShape() -> QTabWidget.TabShape                                               # 获取Tab标签的形状

setTabsClosable(closable:bool) -> None                                          # 设置Tab标签是否可关闭
tabsClosable() -> bool                                                          # 获取Tab标签是否可关闭

setTabsMovable(movable:bool) -> None                                            # 设置Tab标签是否可移动
tabsMovable() -> bool                                                           # 获取Tab标签是否可移动

setDocumentMode(enabled:bool) -> None                                           # 设置Tab标签是否为文档模式
documentMode() -> bool                                                          # 获取Tab标签是否为文档模式

# 槽函数
cascadeSubWindows() -> None                                                     # 层叠显示子窗口
tileSubWindows() -> None                                                        # 平铺显示子窗口

setActiveSubWindow(window:QMdiSubWindow) -> None                                # 设置活跃的窗口
closeActiveSubWindow() -> None                                                  # 关闭正在活动状态的子窗口
closeAllSubWindows() -> None                                                    # 关闭全部子窗口

activatePreviousSubWindow() -> None                                             # 激活上一个子窗口
activateNextSubWindow() -> None                                                 # 激活下一个子窗口

  QMidArea 类常用的信号及其说明如下:

subWindowActivated(window:QMdiSubWindow)                                        # 当子窗口活跃时发射信号

  用 QMdiAreaaddSubWindow(widget:QWidget, flag:Qt.WindowFlags) 方法可以往多文档区中 添加子窗口,并 返回子窗口,参数 widget 可以是子窗口,也可以是其它控件,如果是其它控件,则先创建其它控件,然后在子窗口上添加该控件。用 removeSubWindow(widget:QWidget) 方法可以从多文档中 移除子窗口或子窗口上的控件,使用该方法子窗口或控件并没有真正删除,其父窗口变成 None。如果移除的是控件,则控件所在的子窗口并没有被移除。

  用 QMdiAreasetViewMode(mode:QMdiArea.ViewMode) 方法可以 设置多文档区中子窗口的显示模式,其中参数 modeQMdiArea.ViewMode 类型的枚举值,可以取值如下:

QMdiArea.ViewMode.SubWindowView                                                 # 子窗口视图
QMdiArea.ViewMode.TabbedView                                                    # Tab标签视图

子窗口的显示样式

  在子窗口视图模式下,可以随意缩放和拖动窗口,还可以设置子窗口的排列形式。用 cascadeSubWindows() 方法可以 设置子窗口为层叠排列显示,用 tileSubWindows() 方法可以 设置子窗口为平铺排列显示

  如果用户在界面上单击某个子窗口,则单击的子窗口变成活跃窗口。如果用代码使某个窗口活跃,则要考虑子窗口的顺序。用 setActivationOrder(order:QMdiArea.WindowOrder) 方法可以 设置子窗口的活跃顺序的规则,其中参数 orderQMdiArea.WindowOrder 类型的枚举值,可以取值如下:

QMdiArea.WindowOrder.CreationOrder                                              # 创建顺序
QMdiArea.WindowOrder.StackingOrder                                              # 堆放顺序
QMdiArea.WindowOrder.ActivationHistoryOrder                                     # 历史活跃顺序

  用 QMdiAreasetOption(option:QMdiArea.AreaOption, enable:bool) 方法可以 设置子窗口在活跃时的状态,其中参数 QMdiArea.AreaOption 枚举值只有一个取值 QMdiArea.AreaOption.DontMaximizeSubWindowOnActivation,在 子窗口变成活跃窗口时不进行最大化显示

三、子窗口

  QMdiSubWindow 表示 QMdiArea 中的顶级窗口,它由带有窗口装饰的标题栏、内部小部件以及(取决于当前样式)窗口框架和大小夹点组成。QMdiSubWindow 有自己的布局,它由标题栏和内部小部件的中心区域组成。

  QMdiSubWindow 类常用的方法及其说明如下:

# 实例方法
setWidget(widget:QWidget) -> None                                               # 设置子窗口的控件
widget() -> QWidget                                                             # 获取子窗口的控件

isShaded() -> bool                                                              # 获取子窗口是否处于只显示标题栏状态
mdiArea() -> QMdiArea                                                           # 返回子窗口所在的多文档区域

setSystemMenu(systemMenu:QMenu) -> None                                         # 设置系统菜单
systemMenu() -> QMenu                                                           # 获取系统菜单

setKeyboardPageStep(step:int) -> None                                           # 设置用键盘的Page键控制子窗口移动或缩放时的步长
keyboardPageStep() -> int                                                       # 获取用键盘的Page键控制子窗口移动或缩放时的步长

setKeyboardSingleStep(step:int) -> None                                         # 设置用键盘的方向键控制子窗口移动或缩放时的步长
keyboardSingleStep() -> int                                                     # 获取用键盘的方向键控制子窗口移动或缩放时的步长

setOption(option:QMdiSubWindow.SubWindowOption, on:bool=true) -> None           # 设置选项

# 槽函数
showShaded() -> None                                                            # 只显示标题
showSystemMenu() -> None                                                        # 在标题栏的系统菜单图标下显示系统菜单

  QMdiSubWindow 类常用的信号及其说明如下:

aboutToActivate()                                                               # 当子窗口活跃时发射信号
windowStateChanged(oldState:Qt.WindowState, newState:Qt.WindowState)            # 窗口状态改变时发射信号

  新建一个 ui.py 文件的内容。

from PySide6.QtWidgets import QMainWindow
from PySide6.QtWidgets import QMenuBar, QMdiArea
from PySide6.QtGui import QAction

class MyUi:
    def setupUi(self, window:QMainWindow):
        window.resize(800, 600)                                                 # 1.设置窗口对象大小
  
        self.mdiArea = QMdiArea()                                               # 2.创建MDI窗口,并设置为主窗口的中央控件
        window.setCentralWidget(self.mdiArea)

        self.menuBar = QMenuBar()                                               # 3.创建菜单栏,并设置为主窗口的菜单栏  
        window.setMenuBar(self.menuBar)

        self.menu = self.menuBar.addMenu("子窗体操作")                            # 4.设置菜单,并添加到主窗口的菜单栏中

        self.new_action = QAction("新建", self.menu)                             # 5.创建动作,并添加到菜单中
        self.tile_action = QAction("平铺显示", self.menu)
        self.cascade_action = QAction("级联显示", self.menu)
        self.close_all_window_action = QAction("关闭全部窗口", self.menu)
        self.menu.addActions([self.new_action, self.tile_action, self.cascade_action, self.close_all_window_action])

  新建一个 widget.py 文件的内容。

import sys

from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtWidgets import QMdiSubWindow
from PySide6.QtGui import QAction

from ui import MyUi

class MyWidget(QMainWindow):
    def __init__(self):
        super().__init__()                                                      # 1.调用父类Qwidget类的__init__()方法
      
        self.__ui = MyUi()
        self.__ui.setupUi(self)                                                 # 2.初始化页面

        self.count = 0

        self.__ui.menuBar.triggered.connect(self.menuBar_triggered)             # 3.动作触发时触发信号

    def menuBar_triggered(self, action:QAction):
        if action.text() == "新建":
            self.count += 1

            sub_window = QMdiSubWindow()                                        # 1.创建子窗口对象
            sub_window.setWindowTitle(f"子窗口{self.count}")                     # 2.设置子窗口标题
            sub_window.setFixedSize(300, 200)                                   # 3.设置子窗口大小

            self.__ui.mdiArea.addSubWindow(sub_window)                          # 4.添加子窗口
            sub_window.show()                                                   # 5.显示子窗口
        elif action.text() == "平铺显示":
            self.__ui.mdiArea.tileSubWindows()                                  # 6.平铺显示
        elif action.text() == "级联显示":
            self.__ui.mdiArea.cascadeSubWindows()                               # 7.级联显示
        elif action.text() == "关闭全部窗口":
            self.__ui.mdiArea.closeAllSubWindows()                              # 8.关闭全部子窗口
            windows = self.__ui.mdiArea.subWindowList()                         # 9.获取所有子窗口
            for window in windows:                                              # 10.遍历所有子窗口
                window.deleteLater()                                            # 11.销毁子窗口

if __name__ == "__main__":
    app = QApplication(sys.argv)                                                # 1.创建一个QApplication类的实例
    window = MyWidget()                                                         # 2.创建一个窗口
    window.show()                                                               # 3.显示窗口
    sys.exit(app.exec())                                                        # 4.进入程序的主循环并通过exit()函数确保主循环安全结束

单单调用 closeAllSubWindows() 方法只是关闭窗口而已,窗口对象还是占内存的,所以要用 deleterLater() 方法将其彻底销毁。

posted @ 2025-01-12 18:50  星光映梦  阅读(222)  评论(0)    收藏  举报