4

The documentation for PyQt6 states that

Support for Qt’s resource system has been removed (i.e. there is no pyrcc6).

In light of this, how should one provide resources for a PyQt6 application?

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
biqqles
  • 303
  • 3
  • 10

8 Answers8

14

UPDATE:

As of PyQt-6.3.1, it's possible to use Qt’s resource system again. (This version now includes the qRegisterResourceData and qUnregisterResourceData functions which are required by the generated python resource module.)

There's still no pyrcc6 tool, but Qt's own rcc tool can now be used to convert the qrc file. This tool should be installed by default with a full Qt6 installation, but if you can't find it, you could also use the PySide6 tools to convert the qrc file. (PySide6 simply searches for the Qt6 rcc tool and runs it using subprocess, so it will produce exactly the same output).

Thus, to convert the qrc file, you can now use either:

rcc -g python -o resources.py resources.qrc

or:

pyside6-rcc -o resources.py resources.qrc

However, it's very important to note that the import line at the top of the generated file must be modified to work correctly with PyQt6:

# Resource object code (Python 3)
# Created by: object code
# Created by: The Resource Compiler for Qt version 6.4.0
# WARNING! All changes made in this file will be lost!

# from PySide6 import QtCore <-- replace this line
from PyQt6 import QtCore

The whole operation can be done with this unix one-liner (requires GNU sed):

rcc -g python resources.qrc | sed '0,/PySide6/s//PyQt6/' > resources.py

or:

pyside6-rcc reources.qrc | sed '0,/PySide6/s//PyQt6/' > resources.py  

Once this small change has been made, the generated module can be safely imported into the main application, like this:

from PyQt6 import QtCore, QtGui, QtWidgets
from test_ui import Ui_Window
import resources

class Window(QtWidgets.QWidget, Ui_Window):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

if __name__ == '__main__':

    app = QtWidgets.QApplication(['Test'])
    window = Window()
    window.show()
    app.exec()

Note that it is NOT SAFE to use the generated module without making the changes noted above. This is because the unmodfied module will attempt to import PySide6, which is obviously inappropriate for a PyQt6 application. Whilst it may seem to work on the development machine, there's no guarantee that mixing the two libararies in this way will always remain compatible - and in any case, it's bad practice to enforce the installation of PySide6 on a user's system just so that they can run a PyQt6 application.


OLD ANSWER:

The consensus seems to be that the existing python facilities should be used instead of pyrrc. So the resources would be stored directly in the file-system (perhaps within archive files), and then located using importlib.resources (python >= 3.7), or pkg_resources, or a third-party solution like importlib_resources. Exactly how this maps to existing uses of pyrcc will probably be application-specific, so some experimentation will be needed to find the best approach.

For more details on how to use these facilities, see:

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
11

There has been some discussion on the PyQt mailing list when this was found out.

The maintainer is not interested in maintaining pyrcc anymore as he believes that it doesn't provide any major benefit considering that python already uses multiple files anyway.

The easiest solution is probably to use the static methods of QDir setSearchPaths() or addSearchPath().

The difference will be that resources will be loaded using the prefix used for the methods above.

Considering the previous situation:

icon = QtGui.QIcon(':/icons/myicon.png')

Now it would become like this:

# somewhere at the beginning of your program
QtCore.QDir.addSearchPath('icons', 'path_to_icons/')

icon = QtGui.QIcon('icons:myicon.png')
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Thanks, this is a good solution since it requires minimal modification of any existing code. I imagine the alternative (using a generic resource system) must require something like `QIcon(QPixmap.loadFromData(data))` - and that wouldn't work for vector images. – biqqles Feb 08 '21 at 19:40
  • @biqqles not necessarily: using the [solutions provided by ekhumoro](https://stackoverflow.com/a/66104738/2001654) will still get you access to the file paths in the correct way. Take a look a the whole mail thread linked in the answer, IIRC there should be a couple of messages about that. – musicamante Feb 08 '21 at 19:55
  • I looked through the thread again - do you mean [this message](https://www.riverbankcomputing.com/pipermail/pyqt/2020-September/043228.html) with links to qutebrowser's code? I think I still prefer the neatness of `addSearchPath` but it may well be less robust when it comes to packaging. Both solutions are good but I could only pick one! – biqqles Feb 13 '21 at 21:39
  • When you create an `exe` with `pyinstaller`, then the `path_to_icons` contents are automatically load to the archieve (or must be putted in the exe folder and if yes where)? – Chris P Jan 28 '22 at 22:27
  • Yes. This works flawlessly. Also means I can set the path programatically with my Python code and then access it from qss. I'm very happy with the maintainer's decision. The resource system was a bit clunky. – TheOneTheOnly2 Jul 26 '22 at 12:06
2

for those people who want a real and simple solution just watch it here: link

a guy figured it out by converting the "resource.qrc" into a .py file by using the pyrcc of PySide6. then importing the resource.py (same as before) in your PyQt6 project. everything is the same, including the special filepath syntax: ":/image.jpg" instead of "./image.jpg"

hope it helps, always feels good to have a simpler solution.

  • 2
    This is a bad solution because it forces the installation of PySide6 on the user's system. It should never be necessary to do this in order to run a PyQt6 application. – ekhumoro Oct 29 '22 at 14:37
1

Someone decided pyrcc6 wasn't useful and no longer provide it. However, what they don't understand is how useful it is for those of us that use qt designer to define our resources like icons.. etc and then using pyinstaller to package and find all resources, so that pyinstaller can build a stand-alone exe and these icons are properly embedded and used in the .ui user interface.

You may find this link useful (I had no success with it though): https://pypi.org/project/pyqt6rc/

Ultimately, I switched from pyqt5 to pyside6 using pip which includes pyside6-rcc command to do the same thing that the old pyrcc5 command used to do. BTW pyqt6 has way too many enumeration and that makes transistion to pyqt6 painful, which is another good reason to change to pyside6.

The best full explanation can be found at this YouTube link:

https://www.youtube.com/watch?v=u5BLPTkbaM8

panofish
  • 7,578
  • 13
  • 55
  • 96
0

As I started to use PyQt6, I found missing full support for Qt6 Resources. Especially when using designer and using images for buttons, labels etc. I tried addSearchPath, but still had to edit generated .py template. After some research I found using importlab the best solution for my problem.

I made simple script, which is using .qrc file and generates .py templates with importpath.

For example changing:

icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/icon1.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)

to:

icon = QtGui.QIcon()
with path("myPackage.resources.icons", "icon1.png") as f_path:
    icon.addPixmap(QtGui.QPixmap(str(f_path)), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)

Here is a link to GitLab repo: https://github.com/domarm-comat/pyqt6rc

Or install via pip:

python3 -m pip install pyqt6rc
Domarm
  • 2,360
  • 1
  • 5
  • 17
0

Adding further to youtube video (Update 28/12/22) https://www.youtube.com/watch?v=u5BLPTkbaM8

Do it with the simple script:

pyuic6 tip.ui > tip.py && sed -i '10iimport _cf_rc\nimport _rc_rc' tip.py

Explaination here:

sed -i "10i' -> means insert from the 10th line onwards.

I have 2 files to insert in tip.py:

import _cf_rc

import _rc_rc
0

I liked the answer by musicamante, but I could not apply it directly since I use Qt Designer and pyuic6 to generate the python code for the UI. I did however find a workaround. It builds on using a resource file in Qt Designer, since I already did that with the previous PyQt versions.

  1. Make sure that the path of the resources in the resource (.qrc) file is identical to the folder path. In this example, I have icons in a folder called "icons".
    <RCC>
      <qresource prefix="/">
        <file>icons/myicon.png</file>
      </qresource>
    </RCC>
  1. Run pyuic6 to generate the python code.
  2. Patch the generated python code by replacing the resource path e.g. ':/icons/' with 'icons:' in all path strings. I did that with sed, e.g.
    sed 's/:\/icons\//icons:/g' pyuic6_output_file.py > patched_file.py
  1. In your manually written python code, define the prefix using a relative path from the python file location. This makes sure that the paths are searched related to the folder where the python file is located and not relative to a current working folder. That is what is critical to make it work with pyinstaller.
    QtCore.QDir.setSearchPaths("icons", [os.path.join(os.path.dirname(__file__), 'icons')])
  1. For pyinstaller, use the add-data option to add the folder containing the resources, e.g. --add-data ".\icons:.\icons". They are not added automatically by pyinstaller.
SNil
  • 131
  • 1
  • 9
0

For anyone here who is migrating from pyqt5:

The migration from pyqt5 to pyside6 turned out to be easier for me than going from pyqt5 to pyqt6 since we get pyside6-rcc and pyside6-uic

Easy way to add Apple Silicon/M1/M2 support to a legacy app

Alex Xu
  • 46
  • 3
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/34624552) – sanitizedUser Jul 05 '23 at 23:51