Blog

A Brief Tutorial on Qt’s Resource Files

A Brief Tutorial on Qt’s Resource Files

One of the many tools Qt provides for you is what’s known as the “resource compiler.” The idea is that you might have some data (say, an icon or image file) that your application needs. You could place that file in a particular location on the file system, and your application could load it at run time, but you would need to either ensure that it’s there every time the app runs or ensure that the app will still work without it. The resource compiler gives you an alternative: load that file at compile time and bake the data directly into your executable. Then you never need to worry about finding the file at runtime.

It's a simple and useful system that is worth understanding. The first section is an overview of what it is, why it’s useful, and how to use it. The second section details a few of the things I’ve run into that have tripped me up.

Part 1: Details of the Qt Resource System

The Guts of a QRC File

A resource file (usually with a “.qrc” extension) is just XML that allows you to organize the app’s resources to your liking (you don’t need to write XML yourself if you’re using Qt Creator). When they’re compiled into the executable, you won’t have the luxury of a file system to help you organize and identify your files. This is the job of the QRC file, which is processed as follows at compile time:

  • The resource compiler reads the QRC file
  • The resource compiler loads the resources listed in the QRC file
  • The resource compiler generates a C++ source file that contains a huge array of bytes containing the exact bytes of those resources
  • Your regular compiler toolchain compiles the generated C++ source code into an object file that can be linked with the rest of your application
  • Other parts of your code use a special URL notation to reference resource files, and Qt knows how to point it toward the right part of the compiled byte array

Let’s take a quick look at what it does behind the scenes. Let’s say I have a file that the app uses as a background image. The file is 1462239 bytes:

$ wc -c background.png
1462239 background.png

In hex, 1462239 bytes is 0x164fdf. The generated C++ source looks like this:

static const unsigned char qt_resource_data[] = {
    // /home/markl/path/to/background.png
    0x0,0x16,0x4f,0xdf,
    0x89,
    0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,...

Note that this byte array starts with the four-byte length of the file (0x00 0x16 0x4f 0xdf) followed by the exact bytes of the png file (which we can recognize by the standard magic bytes at the beginning of every png file, 0x89 0x50 0x4e 0x47 0x0d 0x0a 0x1a 0x0a). Additional resource files are concatenated onto that same byte array after the background image. That is, the array contains the size of background.png, then the bytes of background.png, then four bytes denoting the length of the next file, then the bytes of the next file, and so on.

Finally, at the bottom of the generated C++ file are some macros and other helpers. Note that you don’t need to interact with anything in this file directly. We’re only peeking behind the curtain here for educational purposes. Qt’s resource compiler will generate that C++ file for you, and your system’s toolchain will handle compiling and linking it. All you need to do is use URLs in your code to reference the resources you want to use.

URLs and aliases

Once the data is compiled into your application, it no longer has a filesystem path for you to refer to it in your code. So, Qt provides a URL format to conveniently identify resource files listed in your QRC file. You can optionally give each data file an easy-to-remember alias so you can refer to it more conveniently (and, later, you might map that same alias to a different resource file so you can change a resource without changing any of your source code). Neat!

Let’s look at some examples. Figure 1 shows an example in which all QML resources are in a directory called “qml_dir,” and all images are in a directory called “image_dir.” In the QRC file (the right half of the figure shows Qt Creator’s QRC editor), the resources are organized into groups based on “prefixes” that you can define yourself. Here, I created three prefixes: ui, images, and icons.

Resources as they are arranged on the file system (left side) and in the QRC file (right side).

Figure 1: Resources as they are arranged on the file system (left side) and in the QRC file (right side)

The URL that you will use has the following general form: qrc:/prefix/file_path_relative_to_qrc_file

Based on the organization of my QRC file, I can refer to each resource using the following URLs:

File path URL
qml_dir/main.qml qrc:/ui/qml_dir/main.qml
image_dir/background.png qrc:/images/image_dir/background.png
image_dir/image1.png qrc:/images/image_dir/image1.png
image_dir/image2.png qrc:/images/image_dir/image2.png
image_dir/icon1.png qrc:/icons/image_dir/icon1.png
image_dir/icon2.png qrc:/icons/image_dir/icon2.png

For example, using a QML Image object to display background.png would look like this:

Image {
    source: "qrc:/images/image_dir/background.png"
}

You also have the option of applying aliases to items in the resource file. Imagine that your graphic design team gives you an image with a name like Main Screen Background (No Transparency)_144p-FINAL v2_11-21-2022 final.png. Perhaps you would black out with rage, and when you awoke, you would recognize this as a great use case for aliases. Let’s give that image a short (readable and type-able) alias, say, bckgnd, which will appear in parentheses at the end of the line in the QRC file (Figure 2):

An alias applied to an image with an absurd file name

Figure 2: An alias applied to an image with an absurd file name

Now, instead of typing

Image {
    source: "qrc:/images/image_dir/Main Screen Background (No Transparency)_144p-FINAL v2_11-21-2022 final.png"
}

We can use only the prefix and the alias. The alias takes the place of the relative file path in the URL, so it has form qrc:/prefix/alias. For example:

Image {
    source: "qrc:/images/bckgnd"
}

The next week, when you get Main Screen Background (No Transparency)_144p-FINAL v2_11-21-2022 final FINAL.png, you can update the QRC file with the new file path but keep the same alias (bckgnd). None of your code needs to change at all, because the URL you use to reference that file doesn't need to change!

Returning to my original example, I might apply the following aliases:

Aliases applied to all resources

Figure 3: Aliases applied to all resources

All resources can now be used in code using nice short URLs:

File path URL
qml_dir/main.qml qrc:/ui/main_screen
image_dir/background.png qrc:/images/bckgnd
image_dir/image1.png qrc:/images/img1
image_dir/image2.png qrc:/images/img2
image_dir/icon1.png qrc:/icons/start_symbol
image_dir/icon2.png qrc:/icons/stop_symbol

Why Would I Do This?

For me, the most compelling reason is to manage QML files in a QtQuick application. You probably don’t want users to be exposed to the actual QML files that make up the UI, so I list all my QML files as resources. The application is probably useless without the QML files, so you’re likely to want to ensure that they can always be found, and that they can’t change.

Another use case, as described above, is to provide some abstraction between the URL that you use to refer to resources in code and the file name that it has on disk. This helps you manage files with horrific names and allows you to easily update the file path of an aliased file without changing any of your code.

You might also benefit from the reduced amount of code you need to write & maintain. To load a file from disk, you need to know where to look for it. Is it at an absolute path? If so, how do you know what that path is on all systems? Is it a relative path? Relative to what? Once you know what path to look for, what do you do if the file isn’t there? What do you do if it is there, and there’s a problem with permissions? You can neatly sidestep all those problems if you can just compile the file directly into your app and refer to it with an easy-to-remember URL.

Part 2: Limitations and Gotchas

This is all pretty simple, but there are a few things that can get you tripped up that aren’t addressed clearly in the documentation.

Using a full URL instead of an alias

Let’s look at the example above. We’ve got the following:

File path image_dir/image2.png
URL (without the alias) qrc:/images/image_dir/image2.png
URL (using the alias) qrc:/images/img2

Let’s say you’ve recently learned about aliases, and you applied the img2 alias to image2.png. You changed to the shorter URL several places in your code, but you missed one instance and left the full (non-aliased) URL somewhere. Qt’s resource URL resolver will fail to locate that resource. Even though the URL used to be valid, and the relative path to the file has not changed, if you apply an alias, you must use it.

I find this unintuitive. The word “alias” to me implies that you can use it, but you don’t have to. Nevertheless, as of Qt 6.2.1, be aware that this is the case. Note that you can have a mix of aliased and not-aliased items in the same QRC file.

Accidental Alias Collisions

Suppose you accidentally apply the same alias to different items in a QRC file. For example, you alias both icon1.png and icon2.png as “start_symbol.” You will be able to use qrc:/icons/start_symbol as usual in your code, and no errors or warnings will appear at compile time or run time. This is, in fact, normal and useful behavior, but if you aren’t checking for it, it could cause a subtle bug.

Why is this normal and useful? Because one of the common uses for resource files is to provide language translation features. Say you have an image of a stop sign for users in the US (“STOP”) and an image of a stop sign for users in Mexico (“ALTO”). You can use the same alias to refer to one of many resources, and Qt will choose the appropriate image based on the user’s configured locale. We won’t dig into it any more here, but for more information on advanced usage and localization, see the Qt documentation.

Multiple QRC files

You might be tempted, as I was, to separate your resources into multiple QRC files. Each QRC file needs to generate a C++ source file, and then that file needs to be compiled, so you might think that it makes sense to put your QML files (which will change frequently as you develop) in their own QRC file separate from icons, images, and other relatively large files that won’t change much. This makes a lot of sense and seems like a perfectly fine practice... as long as you realize that there is no mechanism for addressing a particular QRC file in your URLs.

Recall that the format of a URL is qrc:/prefix/file_path_relative_to_qrc_file or qrc:/prefix/alias. Nowhere in there do you have the option of specifying a particular QRC file, only the prefix and path or alias. It will still work, and it will not complain if both QRC files each contain the same prefix and the same alias, so be careful that you don’t have colliding prefixes or aliases. There’s not a lot of error checking being done, so be careful!

Big Files

Recall from the first section that the resource compiler puts the bytes of your files into an array of bytes in a C++ source file. As you might imagine, that array can get pretty big. I have seen some instances (generally on smaller ARM systems) in which the generated (huge) C++ file fails to compile without a terribly helpful error message. In this case, you can direct the resource compiler to skip the C++ source file step and compile your resources directly into an object file.

By default, in CMake, you can just list your resource file with the rest of your sources and tell it to automatically run the resource compiler (rcc):

set(CMAKE_AUTORCC ON)       # Run rcc automatically
set(CPP_SRC main.cpp)       # These are my C++ source files
set(QRC_SRC resources.qrc)  # These are my QRC files
qt_add_executable(${PROJECT_NAME} MANUAL_FINALIZATION ${CPP_SRC} ${QRC_SRC})

To skip the C++ generation step, use the qt_add_big_resources() function:

set(CMAKE_AUTORCC ON)                        # Run rcc automatically
set(CPP_SRC main.cpp)                        # These are my C++ source files
qt_add_big_resources(QRC_SRC resources.qrc)  # These are my QRC files
qt_add_executable(${PROJECT_NAME} MANUAL_FINALIZATION ${CPP_SRC} ${QRC_SRC})

The resource compiler will then produce object files without bothering with the intermediate C++ source. Most of the time, you might as well just use qt_add_big_resources()... you know what’s in the generated C++ source now, and it’s probably not that useful to you.

Summary

Qt’s resource compiler can be a nice way to simplify access to data files in your application. You just need to understand the rules regarding URL formats, and make note of a few potential pitfalls:

  • Nothing’s stopping you from giving multiple things the same alias, so be careful
  • Nothing’s stopping you from having multiple QRC files, and nothing’s stopping you from duplicating prefixes and aliases across multiple files
  • If you applied an alias to a resource, you must use it in the URLs
  • Unless you’re especially interested in looking at the C++ source file generated by the resource compiler, feel free to skip that step and use the qt_add_big_resources() function in CMake

Building a Qt app?

I’d love to help! Give us a call or send us an email to discuss!

Comments

There are currently no comments, be the first to post one.

Post a comment

Name (required)

Email (required)

CAPTCHA image
Enter the code shown above:

Related Blog Posts