Qt 资源系统是一种与平台无关的机制,用于在应用程序的可执行文件中存储二进制文件。如果您的应用程序始终需要一组特定的文件(图标,翻译文件等),并且您不想冒丢失文件的风险,这将很有用。
资源系统基于 qmake, rcc(Qt的资源编译器)和 QFile 之间的紧密合作。
Resource Collection Files (.qrc
)
与应用程序关联的资源以 .qrc
文件指定,.qrc
文件是一种基于 XML 的文件格式,该文件格式列出了磁盘上的文件,并可以选择为它们分配一个资源名称,应用程序必须使用该资源名称来访问该资源。
这是一个示例 .qrc
文件:
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/copy.png</file>
<file>images/cut.png</file>
<file>images/new.png</file>
<file>images/open.png</file>
<file>images/paste.png</file>
<file>images/save.png</file>
</qresource>
</RCC>
.qrc
文件中列出的资源文件是属于应用程序源树的一部分的文件。指定的路径是相对于包含.qrc
文件的目录的。请注意,列出的资源文件必须与.qrc
文件位于同一目录或其子目录之一。
资源数据既可以编译为二进制文件,从而可以在应用程序代码中立即访问,也可以创建二进制资源,并在以后的某个时间在应用程序代码中向资源系统注册。
默认情况下,资源可以在应用程序中以与源树中相同的文件名(带有 :/
前缀)或具有 qrc
方案的URL进行访问。
例如,文件路径 :/images/cut.png
或 URL qrc:///images/cut.png
可以访问 cut.png 文件,该文件在应用程序源代码树中的位置为 images/cut.png
。 可以使用文件标签的别名属性来更改它:
<file alias="cut-img.png">images/cut.png</file>
然后,可以从应用程序中以 :/cut-img.png
格式访问该文件。也可以使用 qresource
标记的 prefix 属性为 .qrc
文件中的所有文件指定路径前缀:
<qresource prefix="/myresources">
<file alias="cut-img.png">images/cut.png</file>
</qresource>
在这种情况下,可以通过 :/myresources/cut-img.png
访问该文件。
一些资源需要根据用户的语言环境进行更改,例如翻译文件或图标。这是通过将 lang
属性添加到 qresource
标记,并指定合适的语言环境字符串来完成的。 例如:
<qresource>
<file>cut.jpg</file>
</qresource>
<qresource lang="fr">
<file alias="cut.jpg">cut_fr.jpg</file>
</qresource>
如果用户的语言环境为法语(即 QLocale::system().name() 返回 "fr_FR"
),则 :/cut.jpg
成为对 cut_fr.jpg 图像的引用。对于其他语言环境,使用 cut.jpg。
有关用于语言环境字符串的格式的说明,请参见QLocale文档。
有关选择操作系统特定的功能和其他功能的信息,请参见QFileSelector,以了解选择特定于语言环境的资源的其他机制。
External Binary Resources
对于要创建的外部二进制资源,必须通过将 -binary
开关传递给 rcc 来创建资源数据(通常使用 .rcc
扩展名)。创建二进制资源后,您可以使用 QResource API 注册该资源。
例如,可以通过以下方式来编译.qrc
文件中指定的一组资源数据:
rcc -binary myresource.qrc -o myresource.rcc
在应用程序中,该资源将使用以下代码进行注册:
QResource::registerResource("/path/to/myresource.rcc");
Compiled-In Resources
为了将资源编译成二进制文件,必须在应用程序的 .pro
文件中提及 .qrc
文件,以便 qmake 知道。 例如:
RESOURCES = application.qrc
qmake 将生成 make 规则以生成一个名为 qrc_application.cpp
的文件,该文件链接到应用程序中。该文件包含图像的所有数据和其他资源,它们是压缩二进制数据的静态 C++ 数组。每当.qrc
文件更改或它引用的文件之一更改时,都会自动重新生成qrc_application.cpp
文件。如果不使用.pro
文件,则可以手动调用 rcc 或向构建系统添加构建规则。
当前,Qt 始终将数据直接存储在可执行文件中,即使在 Windows,macOS 和 iOS 上,操作系统也提供对资源的本机支持。这可能会在将来的 Qt 版本中更改。
Compression
rcc 尝试压缩内容以优化最终二进制文件中的磁盘空间使用率。默认情况下,它将执行启发式检查以确定压缩是否值得,并且如果压缩不充分,将存储未压缩的内容。要控制阈值,可以使用-threshold
选项,该选项告诉 rcc 以压缩格式存储文件时必须获得的原始文件大小的百分比。
rcc -threshold 25 myresources.qrc
默认值为“ 70”,表示压缩文件必须比原始文件小 70%(不超过原始文件大小的30%)。
如果需要,可以关闭压缩。 如果您的资源已经包含压缩格式(例如 .png
文件),并且您不想在构建时招致CPU 开销以确认无法压缩,则这将很有用。另一个原因是,如果磁盘使用率不是问题,并且应用程序希望在运行时将内容保留为干净的内存页。 您可以通过提供 -no-compress
命令行参数来实现。
rcc -no-compress myresources.qrc
rcc 还使您可以控制压缩级别和压缩算法,例如:
rcc -compress 2 -compress-algo zlib myresources.qrc
还可以将 threshold
, compress
, 和 compress-algo
用作 .qrc
的 file
标记中的属性。
<qresource>
<file compress="1" compress-algo="zstd">data.txt</file>
</qresource>
对Zstandard和zlib的支持是可选的。如果在编译时未检测到给定的库,则尝试对该库传递-compress-algo
将会导致错误。如果启用了默认压缩算法,则为zstd;如果未启用,则为zlib。
Using Resources in the Application
在应用程序中,可以在大多数地方使用资源路径,而不是普通的文件系统路径。 特别是,您可以将资源路径而不是文件名传递给 QIcon, QImage, 或者 QPixmap 构造函数:
cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this);
有关使用 Qt 资源系统存储其图标的实际应用程序,请参见Application示例。
在内存中,资源由资源对象树表示。该树在启动时自动构建,并由QFile用于解析资源路径。您可以使用以 ":/"
初始化的QDir从根目录浏览资源树。
Qt的资源支持搜索路径列表的概念。然后,如果您使用 :
而不是 :/
作为前缀引用资源,则将使用搜索路径列表查找该资源。启动时搜索路径列表为空; 调用QDir::addSearchPath()添加路径。
Using Resources in a Library
如果库中有资源,则需要通过使用.qrc文件的基本名称调用Q_INIT_RESOURCE()来强制初始化资源。例如:
这样可以确保在静态链接的情况下将资源链接到最终的应用程序二进制文件中。您应该将初始化代码放在库中使用资源的位置附近,以便库客户端仅在使用依赖于资源的库功能时才链接资源。
注意:由于rcc生成的资源初始化程序是在全局名称空间中声明的,因此您对Q_INIT_RESOURCE()的调用也需要在任何名称空间之外进行。
如果库包含的资源不是内部使用的,而是暴露给库的客户端的,则初始化需要在应用程序代码中进行。例如:
与以前一样,这可以确保在静态链接的情况下将资源链接到最终的应用程序二进制文件中,但在动态链接的情况下(例如插件)也可以触发库的加载。
同样,如果必须显式卸载一组资源(因为正在卸载插件或资源不再有效),则可以通过使用与上述相同的基本名称调用Q_CLEANUP_RESOURCE()来强制删除资源。
注意:当资源作为应用程序的一部分构建时,不需要使用Q_INIT_RESOURCE() 和 Q_CLEANUP_RESOURCE()。
pyside2-rcc
首先,有一个名为 icons.qrc
的 .qrc
文件:
</ui>
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>icons/play.png</file>
<file>icons/pause.png</file>
<file>icons/stop.png</file>
<file>icons/previous.png</file>
<file>icons/forward.png</file>
</qresource>
</RCC>
将其转换为 .py
文件:
pyrcc5 icons.qrc -o rc_icons.py # pyside2-rcc icons.qrc -o rc_icons.py
-o
选项使您可以指定输出文件名,在这种情况下为 rc_icons.py
。
要使用生成的文件,请在主 Python 文件顶部添加以下导入:
import rc_icons
Changes in the code
修改现有示例时,需要修改以下几行:
from PySide2.QtGui import QIcon, QKeySequence
playIcon = self.style().standardIcon(QStyle.SP_MediaPlay)
previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward)
pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause)
nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward)
stopIcon = self.style().standardIcon(QStyle.SP_MediaStop)
并将其替换为以下内容:
from PySide2.QtGui import QIcon, QKeySequence, QPixmap
playIcon = QIcon(QPixmap(":/icons/play.png"))
previousIcon = QIcon(QPixmap(":/icons/previous.png"))
pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
nextIcon = QIcon(QPixmap(":/icons/forward.png"))
stopIcon = QIcon(QPixmap(":/icons/stop.png"))
这样可以确保使用新图标代替应用程序主题提供的默认图标。请注意,这些行不是连续的,而是在文件的不同部分中。
在所有导入之后,添加以下内容:
import rc_icons
现在,您的类的构造函数应如下所示:
def __init__(self):
super(MainWindow, self).__init__()
self.playlist = QMediaPlaylist()
self.player = QMediaPlayer()
toolBar = QToolBar()
self.addToolBar(toolBar)
fileMenu = self.menuBar().addMenu("&File")
openAction = QAction(QIcon.fromTheme("document-open"),
"&Open...", self, shortcut=QKeySequence.Open,
triggered=self.open)
fileMenu.addAction(openAction)
exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit",
self, shortcut="Ctrl+Q", triggered=self.close)
fileMenu.addAction(exitAction)
playMenu = self.menuBar().addMenu("&Play")
playIcon = QIcon(QPixmap(":/icons/play.png"))
self.playAction = toolBar.addAction(playIcon, "Play")
self.playAction.triggered.connect(self.player.play)
playMenu.addAction(self.playAction)
previousIcon = QIcon(QPixmap(":/icons/previous.png"))
self.previousAction = toolBar.addAction(previousIcon, "Previous")
self.previousAction.triggered.connect(self.previousClicked)
playMenu.addAction(self.previousAction)
pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
self.pauseAction = toolBar.addAction(pauseIcon, "Pause")
self.pauseAction.triggered.connect(self.player.pause)
playMenu.addAction(self.pauseAction)
nextIcon = QIcon(QPixmap(":/icons/forward.png"))
self.nextAction = toolBar.addAction(nextIcon, "Next")
self.nextAction.triggered.connect(self.playlist.next)
playMenu.addAction(self.nextAction)
stopIcon = QIcon(QPixmap(":/icons/stop.png"))
self.stopAction = toolBar.addAction(stopIcon, "Stop")
self.stopAction.triggered.connect(self.player.stop)
playMenu.addAction(self.stopAction)
# many lines were omitted