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 下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
二、多文档区
多文档区 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) # 当子窗口活跃时发射信号
用 QMdiArea 的 addSubWindow(widget:QWidget, flag:Qt.WindowFlags) 方法可以往多文档区中 添加子窗口,并 返回子窗口,参数 widget 可以是子窗口,也可以是其它控件,如果是其它控件,则先创建其它控件,然后在子窗口上添加该控件。用 removeSubWindow(widget:QWidget) 方法可以从多文档中 移除子窗口或子窗口上的控件,使用该方法子窗口或控件并没有真正删除,其父窗口变成 None。如果移除的是控件,则控件所在的子窗口并没有被移除。
用 QMdiArea 的 setViewMode(mode:QMdiArea.ViewMode) 方法可以 设置多文档区中子窗口的显示模式,其中参数 mode 是 QMdiArea.ViewMode 类型的枚举值,可以取值如下:
QMdiArea.ViewMode.SubWindowView # 子窗口视图
QMdiArea.ViewMode.TabbedView # Tab标签视图

在子窗口视图模式下,可以随意缩放和拖动窗口,还可以设置子窗口的排列形式。用 cascadeSubWindows() 方法可以 设置子窗口为层叠排列显示,用 tileSubWindows() 方法可以 设置子窗口为平铺排列显示。
如果用户在界面上单击某个子窗口,则单击的子窗口变成活跃窗口。如果用代码使某个窗口活跃,则要考虑子窗口的顺序。用 setActivationOrder(order:QMdiArea.WindowOrder) 方法可以 设置子窗口的活跃顺序的规则,其中参数 order 是 QMdiArea.WindowOrder 类型的枚举值,可以取值如下:
QMdiArea.WindowOrder.CreationOrder # 创建顺序
QMdiArea.WindowOrder.StackingOrder # 堆放顺序
QMdiArea.WindowOrder.ActivationHistoryOrder # 历史活跃顺序
用 QMdiArea 的 setOption(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()方法将其彻底销毁。

浙公网安备 33010602011771号