Qt资源系统
Qt资源系统是一种跨平台的机制,用于将二进制文件存储在应用程序的可执行文件中。如果您的应用程序始终需要一定的文件集(如图标、翻译文件等),并且不希望丢失这些文件,这将非常有用。
资源系统基于qmake(Qt的构建工具)、rcc(Qt的资源编译器)和QFile之间的紧密协作。
资源集合文件(.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
属性进行更改:
<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
访问。
某些资源需要根据用户的语言环境更改,例如翻译文件或图标。这可以通过在 qresource
标签中添加 lang
属性,并指定适当的区域设置字符串来完成。例如:
<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 以获取另一个机制,用于选择特定于语言环境的资源,以及选择特定于操作系统的和其他功能。
外部二进制资源
要创建外部二进制资源,您必须通过将 -binary 开关传递给 rcc(Qt的资源编译器)来创建资源数据(通常使用 .rcc
扩展名)。一旦创建了二进制资源,您就可以使用 QResource API 注册该资源。
例如,可以按以下方式编译在 .qrc
文件中指定的一组资源数据:
rcc -binary myresource.qrc -o myresource.rcc
在应用程序中,可以使用以下代码注册此资源:
QResource::registerResource("/path/to/myresource.rcc");
编译入二进制文件的资源
要将资源编译到二进制文件中,必须在应用程序的 .pro
文件中提到 .qrc
文件,以便 qmake 知道它。例如:
RESOURCES = application.qrc
qmake 将生成用于生成名为 qrc_application.cpp
的文件的 make 规则,并将其链接到应用程序中。此文件包含所有图像和其他资源的数据,作为压缩二进制数据的静态 C++ 数组。每当 .qrc
文件更改或它引用的文件更改时,qrc_application.cpp
文件都会自动重新生成。如果您不使用 .pro
文件,您可以手动调用 rcc,或者将构建规则添加到您的构建系统中。
Qt for Python 应用程序中的资源
资源集合文件通过使用资源编译器 rcc 转换为 Python 模块:
rcc -g python application.qrc > application_rc.py
在应用程序中,需要导入该模块:
import application_rc.py
压缩
rcc 试图对内容进行压缩,以优化最终二进制文件的磁盘空间使用。默认情况下,它会执行启发式检查,以确定是否值得进行压缩,并在未能充分压缩时将内容存储为未压缩。要控制阈值,您可以使用 -threshold 选项,告诉 rcc 必须获得原始文件大小的百分比,以便将文件存储为压缩形式。
rcc -threshold 25 myresources.qrc
默认值为 “70”,表示压缩文件必须比原始文件小 70%(不超过原始文件大小的 30%)。
如果需要,可以关闭压缩。如果您的资源已经包含了压缩格式,比如 .png 文件,并且您不想在构建时承担确认无法压缩的成本,这可能很有用。另一个原因是,如果磁盘使用量不成问题,而应用程序更希望将内容保持为干净的内存页面,则可以关闭压缩。您可以通过使用 -no-compress 命令行参数来实现。
rcc -no-compress myresources.qrc
rcc 还可以控制压缩级别和压缩算法,例如:
rcc -compress 2 -compress-algo zlib myresources.qrc
还可以在 .qrc file 标签中使用 threshold、compress 和 compress-algo 作为属性。
<qresource>
<file compress="1" compress-algo="zstd">data.txt</file>
</qresource>
上述内容将选择 zstd 算法并使用压缩级别 1。
rcc 支持以下压缩算法和压缩级别:
- best:使用下面列出的最佳算法中的一个,并在其最高压缩级别上获得最大的压缩,以获得最大的压缩,以牺牲大量 CPU 时间进行编译。在 XML 文件中,此值表示应最大压缩文件,而不管 rcc 支持哪些算法。
- zstd:使用 Zstandard 库来压缩内容。有效的压缩级别范围为 1 到 19,1 表示最低压缩(CPU 时间最少),19 表示最高压缩(CPU 时间最多)。默认级别为 14。特殊值 0 告诉 zstd 库选择一个实现定义的默认值。
- zlib:使用 zlib 库来压缩内容。有效的压缩级别范围为 1 到 9,1 表示最低压缩(CPU 时间最少),9 表示最高压缩(CPU 时间最多)。特殊值 0 表示 “无压缩”,不应使用。默认值是实现定义的,但通常为级别 6。
- none:无压缩。这与 -no-compress 选项相同。
对于 Zstandard 和 zlib,支持是可选的。如果在编译时未检测到给定库,则尝试为该库传递 -compress-algo 将导致错误。如果启用了 Zstandard,则默认压缩算法为 zstd,如果未启用,则为 zlib。
在应用程序中使用资源
在应用程序中,可以在大多数地方使用资源路径,而不是普通的文件系统路径。特别是,您可以将资源路径传递给 QIcon、QImage 或 QPixmap 构造函数,而不是文件名:
cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this);
请参阅 Application 示例,了解使用 Qt 资源系统存储其图标的实际应用程序。
在内存中,资源由资源对象树表示。树在启动时自动构建,并由 QFile 用于解析资源路径。您可以使用:/初始化的 QDir 遍历资源树从根部开始。
Qt 的资源支持搜索路径列表的概念。如果您使用 : 而不是:/作为前缀来引用资源,资源将使用搜索路径列表进行查找。搜索路径列表在启动时为空;调用 QDir::addSearchPath() 来将路径添加到搜索路径列表中。
在库中使用资源
如果您的库中有资源,您需要通过调用 Q_INIT_RESOURCE() 并传入 .qrc 文件的基本名称来强制初始化您的资源。例如:
MyClass::MyClass() : BaseClass()
{
Q_INIT_RESOURCE(resources);
QFile file(":/myfile.dat");
...
}
这确保了在静态链接的情况下将资源链接到最终应用程序二进制文件中。您应该将初始化代码放在库中使用资源的地方附近,以便库的客户端仅在使用依赖于资源的库功能时链接资源。
注意:由于 rcc 生成的资源初始化器声明在全局命名空间中,您对 Q_INIT_RESOURCE() 的调用也需要在任何命名空间之外执行。
如果库包含未在内部使用而是暴露给库的客户端使用的资源,则初始化需要在应用程序代码中进行。例如:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Q_INIT_RESOURCE(graphlib);
QFile file(":/graph.png");
...
return app.exec();
}
与以前一样,这确保了在静态链接的情况下将资源链接到最终应用程序二进制文件中,但也在动态链接的情况下触发了库的加载,例如插件。
类似地,如果您必须显式卸载一组资源(因为插件正在卸载或资源不再有效),您可以通过调用 Q_CLEANUP_RESOURCE() 并传入与上述相同的基本名称来强制删除您的资源。
注意:在将资源作为应用程序的一部分构建时,不需要使用 Q_INIT_RESOURCE() 和 Q_CLEANUP_RESOURCE()。