22. 主窗口控件
一、主窗口控件
QMainWindow
窗口与 QWidget
窗口的最大区别在于窗口上的控件和控件的布局。QMainWindow
窗口通常当作主窗口使用,在它上面除了可以添加 菜单栏、工具栏、状态栏 外,还可以建立 可浮动和可停靠的窗口、中心控件(CentralWidget)、多文档区 和 子窗口。
QMainWindow
窗口的布局如下所示:
一般在 顶部 放置 菜单栏,在 底部 放置 状态栏,在 中心位置 放置一个 控件,控件类型任意,在 中心控件的四周 可以放置 可停靠控件,在 可停靠控件的四周 是 工具栏放置区。需要注意的是,QMainWindow
窗口需要有个中心控件。 QMainWindow
的中心窗口可以是单窗口,也可以是多窗口,多窗口需要把 QMdiArea
控件作为中心控件。中心控件为主显示区,工具栏和可停靠控件可以用鼠标进行拖拽、悬浮和停靠操作。
我们可以在终端中使用 pip
安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip
指令后通过 -i
指定国内镜像源下载。
pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple
国内常用的 pip
下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
QMainWindow
主窗口是从 QWidget
类继承而来的。用 QMainWindow
创建主窗口实例的方法如下所示:
QMainWindow(parent:QWidget=None)
QMainWindow(parent:QWidget=None, flags:Qt.WindowFlags=Default(Qt.WindowFlags))
其中参数 parent
通常不用设置,当作独立窗口使用。
【1】、对中心控件的设置
setCentralWidget(widget:QWidget) -> None # 设置中心控件
centralWidget() -> QWidget # 获取中心控件
takeCentralWidget() -> QWidget # 移除中心控件
【2】、对菜单栏的设置
setMenuBar(menubar:QMenuBar) -> None # 设置菜单栏
menuBar() -> QMenuBar # 获取菜单栏
setMenuWidget(menubar:QWidget) -> None # 设置菜单栏控件
menuWidget() -> QWidget # 获取菜单栏控件
createPopupMenu() -> QMenu # 创建弹出菜单
【3】、对状态栏的设置
setStatusBar(statusbar:QStatusBar) -> None # 设置状态栏
statusBar() -> QStatusBar # 获取状态栏
我们可以使用
setStatusBar(None)
方法可以删除状态栏。
【4】、对工具栏的设置
addToolBar(title:str) -> QToolBar # 添加工具栏,并返回新创建的工具栏
addToolBar(toolbar:QToolBar) -> None # 在顶部添加工具栏
addToolBar(area:Qt.ToolBarArea, toolbar:QToolBar) -> None # 在指定区域添加工具栏
insertToolBar(before:QToolBar, toolbar:QToolBar) -> None # 在第一个工具条前插入工具条
removeToolBar(toolbar:QToolBar) -> None # 移除工具栏
toolBarBreak(toolbar:QToolBar) -> bool # 获取工具栏是否分割
addToolBarBreak(area:Qt.ToolBarArea=Qt.TopToolBarArea) -> None # 添加工具条放置区域,两个工具栏可以并排或者并列显示
insertToolBarBreak(before:QToolBar) -> None # 在某个工具条前插入放置区域
removeToolBarBreak(before:QToolBar) -> None # 移除工具栏前的放置区域
toolBarArea(toolbar:QToolBar) -> Qt.ToolBarArea # 获取工具栏的停靠区
setToolButtonStyle(toolButtonStyle:Qt.ToolButtonStyle) -> None # 设置工具栏按钮样式
toolButtonStyle() -> Qt.ToolButtonStyle # 获取工具栏按钮样式
QMainWindow
可以有一个或多个工具栏,而且工具栏可以拖放到上、下、左、右不同的停靠区。我们可以用 addToolBar(area:Qt.ToolBarArea, toolBar:QToolBar)
方法可以 在指定位置放置已经定义好的工具栏,其中参数 area
是 Qt.ToolBarArea
类型的枚举值,可以取值如下:
Qt.ToolBarArea.LeftToolBarArea # 左侧
Qt.ToolBarArea.RightToolBarArea # 右侧
Qt.ToolBarArea.TopToolBarArea # 顶部,菜单栏下部
Qt.ToolBarArea.BottomToolBarArea # 底部,状态栏上部
Qt.ToolBarArea.AllToolBarAreas # 所有区域都可以停靠
Qt.ToolBarArea.NoToolBarArea # 不可停靠
【5】、停靠控件设置
addDockWidget(area:Qt.DockWidgetArea, dockwidget:QDockWidget) -> None # 在指定停靠区域添加停靠控件
addDockWidget(area:Qt.DockWidgetArea, dockwidget:QDockWidget, orientation:Qt.Orientation) -> None # 在指定停靠区域添加停靠控件,并指定停靠方向
removeDockWidget(dockwidget:QDockWidget) -> bool # 移除停靠控件
restoreDockWidget(dockwidget:QDockWidget) -> bool # 恢复停靠控件
dockWidgetArea(dockwidget:QDockWidget) -> Qt.DockWidgetArea # 获取停靠控件的停靠区域
isDockNestingEnabled() -> bool # 获取停靠区是否可放一个控件
restoreState(state:QByteArray, version:int=0) -> bool # 恢复停靠控件的状态
saveState(version:int=0) -> QByteArray # 保存停靠控件的状态
isAnimated() -> bool # 获取停靠控件是否使用动画效果
setCorner(corner:Qt.Corner, area:Qt.DockWidgetArea) -> None # 设置某个角落属于停靠区的哪一部分
corner(corner:Qt.Corner) -> Qt.DockWidgetArea # 获取某个角落属于停靠区的哪一部分
setTabPosition(areas:Qt.DockWidgetArea, tabPosition:Qt.TabPosition) -> None # 多个停靠控件重叠时,设置Tab标签的位置,默认在底部
setTabShape(tabShape:Qt.TabShape) -> None # 多个停靠控件重叠时,设置Tab标签的形状
splitDockWidget(after:QDockWidget, dockwidget:QDockWidget, orientation:Qt.Orientation) -> None # 将被挡住的停靠控件分为两部分
tabifyDockWidget(first:QDockWidget, second:QDockWidget) -> None # 将第二个停靠控件放在第一个停靠控件的上部,通常创建停靠控件
tabifiedDockWidgets(dockwidget:QDockWidget) -> List[QDockWidget] # 获取停靠区中停靠控件列表
# 槽方法
setDockNestingEnabled(enabled:bool) -> None # 设置停靠区是否可容纳多个控件
setAnimated(enabled:bool) -> None # 设置停靠控件是否使用动画效果
在主窗口中用 addDockWidget(area:Qt.DockWidgetArea, dockwidget:QDockWidget)
方法或 addDockWidget(area:Qt.DockWidgetArea, dockwidget:QDockWidget, orientation:Qt.Orientation)
方法可以 在指定停靠区域添加停靠控件,其中参数 area
是 Qt.DockWidgetArea
类型的枚举值,可以取值如下:
Qt.DockWidgetArea.LeftDockWidgetArea # 左侧
Qt.DockWidgetArea.RightDockWidgetArea # 右侧
Qt.DockWidgetArea.TopDockWidgetArea # 顶部
Qt.DockWidgetArea.BottomDockWidgetArea # 底部
Qt.DockWidgetArea.AllDockWidgetAreas # 所有区域都可以停靠
Qt.DockWidgetArea.NoDockWidgetArea # 不可停靠
参数 orientation
是 Qt.Orientation
类型的枚举值,可以取值如下:
Qt.Orientation.Horizontal
Qt.Orientation.Vertical
如果在一个停靠区域内放置多个停靠控件,通常用 QTabWidget
控件的形式将多个停靠控件层叠在一起。如果要 在一个停靠区内并排或并列放置停靠控件,需要用 setDockNestingEnabled(enable:bool)
方法进行设置。
我们可以用 setTabPosition(area:Qt.DockWidgetArea, position:QTabWidget.TabPosition)
方法 设置多个停靠控件层叠时 Tab 标签的位置,其中参数 position
是 QTabWidget.TabPosition
类型的枚举值,可以取值如下:
QTabWidget.TabPosition.North # 上
QTabWidget.TabPosition.South # 下
QTabWidget.TabPosition.East # 左
QTabWidget.TabPosition.West # 右
我们还可以使用 setTabShape(shape:QTabWidget.TabShape)
方法 设置 Tab 标签的形状,其中参数 shape
是 QTabWidget.TabShape
类型的枚举值,可以取值如下:
QTabWidget.TabShape.Rounded # 圆角。
QTabWidget.TabShape.Triangular # 三角形
主窗口 QMainWindow
的常用信号如下:
iconSizeChanged(iconSize:QSize) # 当工具栏按钮的尺寸发生变化时发射信号
tabifiedDockWidgetActivated(dockWidget:QDockWidget) # 重叠的停靠控件激活时发射信号
toolButtonStyleChanged(toolButtonStyle:Qt.ToolButtonStyle) # 当工具栏按钮的样式发生变化时发射信号
新建一个 ui.py 文件,用来存放 UI 相关的代码。
from PySide6.QtWidgets import QMainWindow
from PySide6.QtWidgets import QMenuBar, QToolBar, QStatusBar
from PySide6.QtWidgets import QToolButton, QComboBox, QLabel
from PySide6.QtGui import Qt, QAction, QIcon, QKeySequence
class MyUi:
def setupUi(self, window:QMainWindow):
window.resize(800, 600) # 1.设置窗口对象大小
self.menuBar = QMenuBar() # 2.创建菜单栏,并添加到窗口中
window.setMenuBar(self.menuBar)
self.toolBar = QToolBar() # 3.创建一个工具栏,并添加到窗口中
window.addToolBar(self.toolBar)
self.statusBar = QStatusBar() # 4.创建一个状态栏,并添加到窗口中
window.setStatusBar(self.statusBar)
self.label = QLabel()
window.setCentralWidget(self.label)
self.file_menu = self.menuBar.addMenu("文件(&F)") # 5.向菜单栏中添加菜单
# 6.创建一个动作
new_file_action = QAction("新建文件", window, icon=QIcon("assets/images/1.ico"), shortcut=QKeySequence.fromString("Ctrl+N"))
new_folder_action = QAction("新建文件夹", window, icon=QIcon("assets/images/1.ico"))
open_file_action = QAction("打开文件", window, icon=QIcon("assets/images/1.ico"), shortcut=QKeySequence.fromString("Ctrl+O"))
open_folder_action = QAction("打开文件夹", window, icon=QIcon("assets/images/1.ico"))
save_action = QAction("保存", window, icon=QIcon("assets/images/1.ico"), shortcut=QKeySequence.fromString("Ctrl+S"))
save_as_action = QAction("另存为", window, icon=QIcon("assets/images/1.ico"), shortcut=QKeySequence.fromString("Ctrl+Shift+S"))
save_all_action = QAction("全部保存", window, icon=QIcon("assets/images/1.ico"))
self.file_menu.addActions([new_file_action, new_folder_action])
self.file_menu.addSeparator() # 7.添加分隔符
self.file_menu.addActions([open_file_action, open_folder_action])
self.file_menu.addSeparator()
self.file_menu.addActions([save_action, save_as_action, save_all_action])
self.file_menu.addSeparator()
self.toolBar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextUnderIcon) # 8.设置工具栏按钮的样式
self.toolBar.addActions(self.file_menu.actions())
comboBox = QComboBox() # 9.添加其它组件
names = ["木之本樱", "御坂美琴", "夏娜"]
comboBox.addItems(names)
self.toolBar.addWidget(comboBox)
toolButton = QToolButton() # 10.添加工具按钮
toolButton.setText("文件")
toolButton.setArrowType(Qt.ArrowType.DownArrow) # 11.设置工具按钮的箭头样式
toolButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) # 12.设置工具按钮的样式
toolButton.addActions(self.file_menu.actions())
self.toolBar.addWidget(toolButton) # 13.工具栏中添加工具按钮
新建一个 widget.py 文件,用来存放业务逻辑相关的代码。
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QTimer, QDateTime
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.初始化页面
timer = QTimer(self) # 3.创建一个QTime定时器对象
timer.start(1000) # 4.启动定时器
timer.timeout.connect(self.update_time) # 5.定时器定时时间到触发
self.__ui.menuBar.hovered.connect(self.action_hovered) # 6.鼠标悬停菜单时触发信号
self.__ui.menuBar.triggered.connect(self.action_triggered) # 7.动作被激活时触发信号
self.__ui.toolBar.actionTriggered.connect(self.action_triggered) # 8.工具栏动作被激活时触发信号
def update_time(self):
datatime = QDateTime.currentDateTime() # 1.获取当前日期时间
text = datatime.toString("yyyy-MM-dd HH:mm:ss") # 2.对日期时间进行格式化
self.__ui.statusBar.showMessage(f"当前日期时间: {text}") # 3.在状态栏中显示一条临时消息
def action_hovered(self, action:QAction):
text = f"你悬停在【{action.text()}】菜单项"
self.__ui.label.setText(text)
def action_triggered(self, action:QAction):
text = f"你点击了【{action.text()}】菜单项"
self.__ui.label.setText(text)
if __name__ == "__main__":
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
window = MyWidget() # 2.创建一个窗口
window.show() # 3.显示窗口
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
二、停靠控件
停靠控件 QDockWidget
主要应用在主窗口中,用鼠标可以将其拖拽到不同的停靠区域中。停靠控件通常作为容器来使用,需要在其内部添加一些常用控件。停靠控件由标题栏和内容区构成,标题栏上显示窗口标题,还有浮动按钮和关闭按钮。
停靠控件 QDockWidget
继承自 QWidget
。用 QDockWidget
类创建停靠控件实例的方法如下所示:
QDockWidget(parent:QWidget=None, flags:Qt.WindowFlags=Default(Qt.WindowFlags))
QDockWidget(title:str, parent:QWidget=None, flags:Qt.WindowFlags=Default(Qt.WindowFlags))
其中 title
是停靠控件的 窗口标题,parent
是停靠控件所在的 窗口。
停靠控件 QDockWidget
类常用的方法如下:
setAllowedAreas(areas:Qt.DocksWidthArea) -> None # 设置可停靠区域
isAreaAllowed(area:Qt.DocksWidthArea) -> bool # 获取区域是否允许停靠
allowedAreas() -> Qt.DocksWidthArea # 获取可停靠区域
setFeatures(features:Qt.DocksWidthFeatures) -> None # 设置特征
setFloating(floating:bool) -> None # 设置是否浮动状态
setTitleBarWidget(widget:QWidget) -> None # 设置标题栏控件
titleBarWidget() -> QWidget # 获取标题栏控件
setWidget(widget:QWidget) -> None # 添加控件
widget() -> QWidget # 获取控件
toggleViewAction() -> QAction # 获取隐藏或显示的动作
停靠控件 QDockWidget
类常用的信号如下:
allowedAreasChanged(allowedAreas:Qt.DockWidgetAreas) # 允许停靠的区域发生改变时发射信号
dockLocationChanged(area:Qt.DockWidgetArea) # 停靠的区域发生改变时发射信号
featuresChanged(features:QDockWdget.DockWidgetFeature) # 特征改变时发射信号
topLevelChanged(topLevel:bool) # 悬浮和停靠状态转换时发射信号
visibilityChanged(visible:bool) # 可见性改变时发射信号
用 setFeatures(feature:QDockWidget.DockWidgetFeatures)
方法 设置停靠控件的特征,其中参数 feature
是 QDockWidget.DockWidgetFeature
类型的枚举值,可以取值如下:
QDockWidget.DockWidgetFeature.DockWidgetClosable # 可关闭
QDockWidget.DockWidgetFeature.DockWidgetMovable # 可移动
QDockWidget.DockWidgetFeature.DockWidgetFloatable # 可悬停
QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar # 有竖向标题
QDockWidget.DockWidgetFeature.NoDockWidgetFeatures # 没有以上特征
用 toggleViewAction()
方法 返回一个 QAction
动作对象,单击该动作对象可以切换停靠窗口的可见状态,即该动作是一个对停靠控件窗口进行显示或关闭的开关。如果将该动作加到菜单上,对应菜单栏的文字即为停靠窗口的 title 文字,这样就可以在菜单上单击对应菜单项进行停靠窗口的关闭和显示。
修改 ui.py 文件的内容。
from PySide6.QtWidgets import QMainWindow
from PySide6.QtWidgets import QDockWidget
from PySide6.QtWidgets import QFontDialog, QColorDialog
from PySide6.QtGui import Qt
class MyUi:
def setupUi(self, window:QMainWindow):
window.resize(800, 600) # 1.设置窗口对象大小
self.font_dockWidget = QDockWidget("字体", window) # 2.创建停靠控件
self.color_dockWidget = QDockWidget("颜色", window)
window.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.font_dockWidget) # 3.在主窗口中添加停靠控件
window.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.color_dockWidget)
# 4.设置停靠控件的特征
self.font_dockWidget.setFeatures(
QDockWidget.DockWidgetFeature.DockWidgetFloatable |
QDockWidget.DockWidgetFeature.DockWidgetMovable |
QDockWidget.DockWidgetFeature.DockWidgetClosable)
self.color_dockWidget.setFeatures(
QDockWidget.DockWidgetFeature.DockWidgetFloatable |
QDockWidget.DockWidgetFeature.DockWidgetMovable |
QDockWidget.DockWidgetFeature.DockWidgetClosable)
window.tabifyDockWidget(self.font_dockWidget, self.color_dockWidget) # 6.将第二个停靠控件放置第一个停靠控件的上部
self.font_dockWidget.setWidget(QFontDialog()) # 7.往停靠控件中添加控件
self.color_dockWidget.setWidget(QColorDialog())
修改 widget.py 文件的内容。
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from ui import MyUi
class MyWidget(QMainWindow):
def __init__(self):
super().__init__() # 1.调用父类Qwidget类的__init__()方法
self.__ui = MyUi()
self.__ui.setupUi(self) # 2.初始化页面
if __name__ == "__main__":
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
window = MyWidget() # 2.创建一个窗口
window.show() # 3.显示窗口
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束