前言

本系列文章为b站PySide6教程以及官方文档的学习笔记

b站教程视频传送门如下

官方文档链接:Qt for Python

菜单栏

相关控件

  1. QMenuBar: 这是最顶层的菜单栏控件,通常位于窗口的顶部。它作为容纳多个菜单(QMenu 对象)的容器。一个标准的桌面应用程序通常有一个菜单栏,包含如“文件”、“编辑”、“视图”等标准菜单。
  2. QMenu: 这个控件代表菜单栏中的一个单独菜单,例如“文件”菜单。每个 QMenu 对象可以包含多个 QAction 对象,这些对象代表具体的命令或选项,如“打开”、“保存”、“退出”等。
  3. QAction: 这是代表具体操作的控件。它可以是菜单项、工具栏按钮或者是键盘快捷键的触发器。通过 QAction,我们可以定义当用户点击菜单项或按下快捷键时应该执行的动作。

它们的逻辑关系是这样的:QMenuBar 包含多个 QMenu,每个 QMenu 包含多个 QAction。用户通过点击 QAction 来触发具体的功能。

当我们想为窗体添加菜单栏时,窗体类型必须是QMainWindow

菜单栏中的菜单可以出现嵌套关系,即一个菜单中除了操作外还有子菜单

在日常使用的软件中经常会出现这种情况

使用QtDesigner快速构建菜单栏

当我们在QtDesigner在创建一个MainWindow窗口时,顶部会自动放置一个菜单栏

点击编辑栏后即可开始往菜单栏中添加菜单,输入完毕需要敲回车键确认

当我们添加了一个菜单之后,我们可以选择点击右边的编辑栏继续向菜单栏添加菜单,或者点击当前菜单下方的编辑栏,向当前菜单中添加子菜单或操作

菜单中的子菜单在QtDesigner中只能输入英文,如果想要输入中文,可以在属性中设置

点击操作项右边的拓展图标,我们可以将其变为子菜单

接着我们就能在该子菜单中添加操作和子菜单

分隔符在菜单栏的设计中通常是用来将菜单中的不同功能分隔开,使其更加美观,所以说其实可有可无

动作编辑器中,我们可以为具体操作配置图标和快捷键

当然,我们还可以通过右键窗口来添加工具栏

可以直接将已有操作拖入工具栏

结构语法

在设计时,我们一般是先想到有哪些菜单需要实现,再去详细设计这些菜单下的操作

但是在编写代码时,我们需要自底向上,先将操作/子菜单添加到菜单,再将菜单添加到菜单栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()

self.menu = self.menuBar()

self.openFile = QAction('打开文件')
self.closeFile = QAction('关闭文件')

self.moreMenu = QMenu('更多')
self.more1 = QAction('更多1')
self.more2 = QAction('更多2')
self.moreMenu.addAction(self.more1)
self.moreMenu.addAction(self.more2)

self.fileMenu = QMenu('文件')
self.fileMenu.addAction(self.openFile)
self.fileMenu.addAction(self.closeFile)
self.fileMenu.addMenu(self.moreMenu)

self.menu.addMenu(self.fileMenu)

self.mainlayout = QVBoxLayout()
self.setLayout(self.mainlayout)

我们使用addAction()方法将操作添加到菜单中

使用addMenu()方法可以将子菜单添加到菜单中,或将菜单添加到菜单栏中

同时我们也可以将操作项的triggered信号绑定到槽

1
2
3
self.openFile.triggered.connect(lambda: print('打开文件'))
self.closeFile.triggered.connect(lambda: print('关闭文件'))
self.more1.triggered.connect(lambda: print('更多1'))

上下文菜单

概念

上下文菜单即我们在应用程序中右键点出的菜单

image-20231218155228810

这里贴一张Edge浏览器的上下文菜单截图,可以发现与我们的普通菜单的元素非常相似

事实上在代码实现上,上下文菜单的实现逻辑也和菜单栏和相似

为窗体添加上下文菜单

我们可以为窗体本身或者控件添加上下文菜单

先来介绍如何给窗体本身添加

将窗体想象为一个菜单,那么我们就可以向其中添加子菜单和操作

但是在添加之前,我们需要先设置上下文菜单策略

1
2
from PySide6.QtCore import Qt
self.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)

接下来我们直接使用窗体本身的addAction()方法来添加菜单项

1
2
self.addAction(self.openFile)
self.addAction(self.closeFile)

添加菜单项时,也可以使用更简洁的列表添加

1
self.addActions([self.openFile,self.closeFile])

为控件添加上下文菜单

其实许多控件本身是自带上下文菜单的

当我们在页面中添加一个编辑框并右击它时,会出现如下的上下文菜单

包括重做、恢复、复制、粘贴等对输入内容的基本操作

当然,我们也可以修改这个默认的上下文菜单,向其中添加自定义操作

首先我们还是需要为控件设置上下文菜单策略

1
self.lineEdit1.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)

接下来我们对控件使用addActions()方法来添加菜单项

1
2
3
self.sendValue = QAction('发送值到输入框2')
self.showCurrentValue = QAction('显示当前值')
self.lineEdit1.addActions([self.sendValue, self.showCurrentValue])

最后我们可以为这两个操作绑定逻辑

1
2
self.sendValue.triggered.connect(lambda: self.lineEdit2.setText(self.lineEdit1.text()))
self.showCurrentValue.triggered.connect(lambda: print(self.lineEdit1.text()))

折叠菜单

首先规划选项卡中的布局和内容

这里我们写两个选项卡用于演示

1
2
3
4
5
6
7
8
9
10
11
12
#创建折叠选项卡内容
self.widget1 = QWidget()
self.widget1Layout = QVBoxLayout()
self.widget1Layout.addWidget(QLabel('这是第一个选项卡'))
self.widget1Layout.addWidget(QPushButton('按钮1'))
self.widget1.setLayout(self.widget1Layout)

self.widget2 = QWidget()
self.widget2Layout = QVBoxLayout()
self.widget2Layout.addWidget(QLabel('这是第二个选项卡'))
self.widget2Layout.addWidget(QPushButton('按钮2'))
self.widget2.setLayout(self.widget2Layout)

接下来我们创建折叠窗QToolBox,并将选项卡用addItem方法置入

1
2
3
4
5
6
7
self.toolBox = QToolBox()
self.toolBox.addItem(self.widget1, '选项卡1')
self.toolBox.addItem(self.widget2, '选项卡2')

self.mainlayout = QVBoxLayout()
self.mainlayout.addWidget(self.toolBox)
self.setLayout(self.mainlayout)

效果如下

当然我们也可以加上图标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建一个右箭头图标和一个下箭头图标
self.arrowRight = self.style().standardIcon(QStyle.StandardPixmap.SP_ArrowRight)
self.arrowDown = self.style().standardIcon(QStyle.StandardPixmap.SP_ArrowDown)

self.toolBox = QToolBox()
self.toolBox.addItem(self.widget1, self.arrowDown,'选项卡1')
self.toolBox.addItem(self.widget2, self.arrowRight,'选项卡2')
self.toolBox.currentChanged.connect(self.changeArrow)

...

def changeArrow(self, index):
# 全部重置为右箭头
for i in range(self.toolBox.count()):
self.toolBox.setItemIcon(i, self.arrowRight)
# 当前选项卡设置为下箭头
self.toolBox.setItemIcon(index, self.arrowDown)

效果如下

资源的加载

内置图标

PySide6提供了一组内置的图标,我们可以在应用程序中使用这些图标来美化界面或者作为按钮、工具栏等控件的图标。

我们可以通过QStyle类中的standardIcon()方法来访问这些内置图标。

以下是一些常用的内置图标:

  • SP_ArrowUp: 向上箭头
  • SP_ArrowDown: 向下箭头
  • SP_ArrowLeft: 向左箭头
  • SP_ArrowRight: 向右箭头
  • SP_DialogOkButton: 对话框确认按钮
  • SP_DialogCancelButton: 对话框取消按钮
  • SP_FileIcon: 文件图标
  • SP_DirIcon: 文件夹图标
  • SP_ComputerIcon: 计算机图标

要使用这些内置图标,首先需要导入 QStyle

1
from PySide6.QtWidgets import QStyle

在窗口初始化时我们需要获取图标对象

1
self.okIcon = self.style().standardIcon(QStyle.StandardPixmap.SP_DialogOkButton)

最后我们可以将这些图标对象直接应用到需要的控件上

1
2
okButton = QPushButton("OK")   
okButton.setIcon(self.okIcon)

Rcc的使用

当我们打包软件时,一般只有py文件或者是一些源码和环境被打包进exe,而资源文件仍然存在指定目录下的文件夹下

这样往往会显得软件较为臃肿,还容易将资源文件误删

我们可以使用Rcc来将各种资源文件(图像、视频、数据库等)转成py文件,和代码一块打包

在PySide6中,RCC(Resource Compiler)是一个用于将资源文件(如图像、样式表等)编译成二进制格式的工具。编译后的资源文件可以嵌入到Python可执行文件中,以便在运行时轻松访问这些资源。

创建资源文件

我们可以使用Qt Designer来轻松创建和管理这些二进制资源文件

打开Qt Designer,我们可以点击菜单栏的视图>资源浏览器来开启资源视图

此时右下角会多出一个资源浏览器

点击左上角的编辑按钮,即可编辑资源

编辑界面中有三个需要关注的按钮,新建资源文件、添加前缀以及添加文件

这里我们可以将其类比于数据库

每个资源文件,即一个二进制py文件,可以看作是一个库,我们在项目中可以引入这些资源库

而前缀即库中的一些表,可以当作子目录,我们可以将相同类型的资源文件添加到相同前缀中

而文件即具体的资源文件,作为前缀中的一些的表项

创建完毕后,我们在vscode中找到生成的qrc文件,右键该文件点击Compile进行编译

随后即可得到二进制py文件

加载资源文件

当我们想加载得到的二进制资源文件中的资源时,可以直接在Qt Designer中可视化引用

例如我们添加一个按钮后,可以在属性编辑器中选择图标

点击选择资源,即可从我们创建的资源文件库中挑选资源

当然,我们也可以在代码中加载资源

首先引用资源文件库

1
import test_rc

接着我们可以通过特殊的路径引用方法使用库中的资源创建图标,即:/前缀/路径

1
2
check_yes_icon = QIcon()
check_yes_icon.addFile(u":/icons/icons/check_yes.png", QSize(), QIcon.Normal, QIcon.Off)

需要注意的是,这里的路径中,前一个icons为前缀名,而后面的icons/check_yes.png为一开始创建资源文件时,该资源相对于资源文件库的路径

所以其实为了更便捷地引用,在一开始创建资源库时,最好这些资源和库文件放在同一目录下