1.Qt资源系统简介
Qt 资源系统是一种独立于平台的资源管理器,用于在应用程序的可执行文件中存储二进制文件。如果应用程序始终需要一组特定的文件(图标、翻译文件等),使用Qt资源管理系统可以有效防止应用文件丢失。简而言之,Qt资源系统就是桌面应用中的静态文件管理器。
Qt资源系统基于qmake、rcc(Qt 的资源编译器)和QFile。
2.qrc文件
qrc是一种基于XML格式的文件,应用使用的资源文件在拓展名为qrc的文件中声明。qrc文件中声明的资源文件是应用源代码的一部分,其中的文件路径是相对于qrc文件所在目录的相对路径。因此qrc中声明的资源文件必须和qrc文件处于同一目录或者是该目录的子目录。
资源文件可以直接随源代码编译到应用可执行文件中,在代码中直接调用,也可以先创建资源文件然后使用代码注册到资源系统中。例如声明一张图片:
<file>images/cut.png</file>
3.引用资源
声明资源后,可以在程序中用:/前缀或者以qrc为命名的URL访问。例如引用相对于qrc文件所在目录的图片images/cut.png可以用:/images/cut.png或者qrc:///images/cut.png。
如果不想每次都输入这么长的路径,可以使用alias属性,即
<file alias="cut-img.png">images/cut.png</file>
这样可以使用:/cut.png访问图片images/cut.png。
4.路径前缀
使用路径前缀可以将不同目录下的文件组合到一个命名空间中,便于在不移动文件的前提下,以统一的路径前缀访问不同路径下文件,相当于对文件做了虚拟分类。
<qresource prefix="/myresources">
<file alias="cut-img.png">images/cut.png</file>
<file alias="icon.png">icons/icon.png</file>
</qresource>
现在可以使用:/myresources/cut-img.png访问cut-img.png, 使用:/myresources/icon.png访问icon.png。
注意
(1)不加别名时,文件引用方式是:/文件相对于qrc文件路径,例如
<file >images/cut.png</file>
的引用方式是 :/images/cut.png。
(2)加前缀时,文件引用方式是:/前缀/文件相对于qrc文件路径
<file prefix="/icon">images/cut.png</file>
变为:/icon/images/cut.png。
(3)加别名和前缀时是:/前缀/文件别名
<qresource prefix="/icon">
<file alias="cut-img.png">images/cut.png</file>
</qresource>
变为 :/icon/cut.png。
5.文件国际化
有些时候应用可能需要在不同的语言环境下使用不同的文件,使用lang属性可以轻松做到这一点:
<qresource>
<file>cut.jpg</file>
</qresource>
<qresource lang="fr">
<file alias="cut.jpg">cut_fr.jpg</file>
</qresource>
当系统语言切换成法语时,对cut.jpg的引用将会自动切换成cut_fr.jpg,当系统语言是其它语言时,将会引用cut.jpg。
6.编译qrc文件为python模块
要在pyside6项目中引用qrc文件,需要使用rcc工具将其编译为python模块,编译工具是pyside6-rcc。例如下面的qrc文件:
<RCC>
<qresource prefix="/icon">
<file>icon/ic_last_step.svg</file>
<file>icon/ic_next_step.svg</file>
<file>icon/ic_start.svg</file>
</qresource>
</RCC>
在qrc文件所在目录运行 编译命令:
pyside6-rcc res.qrc -o res.py
生成res.py 即资源模块。
7.应用示例
下面以设置应用按钮图标为例说明如何使用Qt资源管理器管理图标文件。图标文件可以从阿里云图标库下载:iconfont-阿里巴巴矢量图标库。
(1)创建图标qrc文件
新建一个pyside6项目,目录结构如下:
将图标保存在resource/icon下。
小技巧
在pycharm中直接编辑qrc文件很繁琐,使用Pyside6开发Qt程序的老铁们不妨装一个官方的QtCreator,这样可以快速编辑很多Qt文件,还有很多代码示例可以看,另外还可以用QtCreator快速生成很多模板代码,排查Qt文件中错误等。
Q:为啥不直接用QtCreator开发Pyside6项目?
A:QtCreator的代码编辑器太垃圾了,使用Pycharm快到飞起。
现在打开QtCreator新建这个res.qrc文件,把图标路径加进来:
创建文件后,先添加一个icon前缀:
然后使用Add Files按钮把图标文件添加到路径中,然后保存:
现在得到以下的qrc文件:res.qrc
<RCC>
<qresource prefix="/icon">
<file>icon/ic_last_step.svg</file>
<file>icon/ic_next_step.svg</file>
<file>icon/ic_start.svg</file>
</qresource>
</RCC>
为了缩短引用路径,给每个图标文件取个别名(alias):
现在得到这样的qrc文件:
<RCC>
<qresource prefix="/icon">
<file alias="ic_last_step">icon/ic_last_step.svg</file>
<file alias="ic_next_step">icon/ic_next_step.svg</file>
<file alias="ic_start">icon/ic_start.svg</file>
</qresource>
</RCC>
(2)编译qrc文件为python模块
在这个qrc文件所在目录运行命令:
pyside6-rcc res.rc -o res.py
如果不在这个目录运行,res.qrc和res.py要改成绝对路径或者相对于命令运行目录的相对路径。
运行完可以看到res.qrc文件所在目录多了一个res.py文件,打开看看:
# Resource object code (Python 3)
# Created by: object code
# Created by: The Resource Compiler for Qt version 6.1.3
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
qt_resource_data =b"......"
qt_resource_name = b"......"
qt_resource_struct = b"......"
def qInitResources():
QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources():
QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources()
可以看到文件中包含三个二进制编码的字符串:
qt_resource_data:qrc源码,其中还包含了矢量图标的svg代码。
qt_resource_name:资源名称;
qt_resource_struct:资源二进制文件结构;
还有两个Python函数:
qInitResources:初始化资源,调用了qRegisterResourceData方法在应用中注册资源。qCleanupResources:清除资源,调用了qUnregisterResourceData方法在应用中卸载资源。
提示
编译完成后,资源源文件可以删除了,比如上述icon中的图标文件可以全部删除。
(2)在程序中引用资源文件
创建main.py
import random
import sys
from PySide6 import QtWidgets, QtCore
from PySide6.QtGui import QPixmap, QIcon
from PySide6.QtWidgets import QGridLayout
import resource.res as res
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.text = QtWidgets.QLabel("Hello World")
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.text)
# 按钮布局
self.btn_layout = QGridLayout()
# 创建三个按钮
self.button_last_step = QtWidgets.QPushButton()
# 按钮图标
self.button_last_step.setIcon(QPixmap(":/icon/ic_last_step"))
self.btn_layout.addWidget(self.button_last_step, 0, 0)
self.button_start = QtWidgets.QPushButton()
self.button_start.setIcon(QPixmap(":/icon/ic_start"))
self.btn_layout.addWidget(self.button_start, 0, 1)
self.button_next_step = QtWidgets.QPushButton()
self.button_next_step.setIcon(QPixmap(":/icon/ic_next_step"))
self.btn_layout.addWidget(self.button_next_step, 0, 2)
# 添加按钮布局到窗口布局中
self.layout.addLayout(self.btn_layout)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = MyWidget()
widget.resize(400, 300)
widget.show()
sys.exit(app.exec())
运行效果: