Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Icons from Resources not showing up in QPushButton or QToolButton



  • I have no icons in QToolButton or QPushButton. I reviewed dozen of articles and number of posts and I still can't figure that out.

    I've created a icons.qrc resource file in a subfolder Resouces of the test project and added number of icons (24x24px, PNG). Main Toolbar's icon size is set to 24x24px. When prompted for prefix, I choosed '/icons' and I named the file Icons. So it looks like this:

    ./
    ├── main.py
    ├── main.pyproject
    ├── main.pyproject.user
    ├── mainwindow.ui
    └── Resources
    .. ├── ...
    .. ├── con.png
    .. ├── ...
    .. ├── icons.qrc
    .. └── ...

    Note: The forum engine brakes formatting of the tree, entries under Resources are children of the Resources directory

    I tried number of ways to use the icons then, but two in general:

    1. I right-clicked an icon file in resource file and choosed "Copy path ':/icons/con.png'" to get the right path into my clipboard. I then tried to use it in my code dynamically as follows:
    self.MainToolBar.addWidget(QPushButton(QIcon(':/icons/con.png'),'NNN'))
    

    or i.e. via PixMap:

        mbtnTestQ = QPushButton()
        mbtnTestQ.setText("Qqqqq")
        mbtnTestQ.setIcon(QIcon(QPixmap(":/icons/con.png")))
        self.MainToolBar.addWidget(mbtnTestQ)
    

    or I also tried following ways to include the path:

        self.MainToolBar.addWidget(QPushButton(QIcon(':/Resources/icons/con.png'),'NNN'))
    
        self.MainToolBar.addWidget(QPushButton(QIcon(':/Resources/icons/icons/con.png'),'NNN'))
    
        self.MainToolBar.addWidget(QPushButton(QIcon(':/icons/icons/con.png'),'NNN'))
    
    1. Using a static button in QtDesigner, I created an action, clicked the three-dot icon to choose icon image. It didn't offer me any resource, so I clicked an icon in ithe dialog toolbar and in the filesystem tree I choosed the resource file I created before. Then I picked an icon and I could see it in the action. I dragged the Action into an empty default toolbar of the QApplication and it looked OK. But when program is executed the icon is not shown. That really strikes me, because while I could easily do something wrong in the code, the GUI should get it right...

    In all cases, buttons appear in the toolbar, but without icons.

    Perhaps, do I have to convert the resource file using pyside2-uic tool, even though the UI file is included directly (without conversion)? Or do I have to import resource file in the header? I remember one tutorial (don't remember which) that stated Resouces have to be converted even if UI file is used directly, but other artciles and comments don't mention anything about conversion.

    So, what is wrong?

    Some of my experimental code is here:

    import sys
    from PySide2 import QtWidgets
    from PySide2.QtUiTools import QUiLoader
    from PySide2.QtWidgets import QApplication, QPushButton, QLineEdit, QFrame, QToolBar, QToolButton, QMenu, QAction, QWidgetAction
    from PySide2.QtCore import QFile, QObject
    from PySide2.QtGui import QIcon, QPixmap
    from PySide2 import QtGui
    # import icons.qrc  # This doesn't seem to be correct and needed
    
    
    class Form(QObject):
    
        def __init__(self, ui_file, parent=None):
            super(Form, self).__init__(parent)
            ui_file = QFile(ui_file)
            ui_file.open(QFile.ReadOnly)
    
            loader = QUiLoader()
            self.window = loader.load(ui_file)
            ui_file.close()
    
            # Toolbar
            self.MainToolBar = self.window.findChild(QToolBar, 'MainToolBar')
            self.MainToolBar.addWidget(QPushButton(QIcon(':/icons/icons/AddRecord.png'),'NNN'))
            self.MainToolBar.addWidget(QPushButton('OOO'))
            self.MainToolBar.addWidget(QPushButton('PPP'))
            self.MainToolBar.addWidget(QPushButton('RRR'))
    
            mbtnTestQ = QPushButton()
            mbtnTestQ.setText("Qqqqq")
            mbtnTestQ.setIconSize(QSize(24,24))
            mbtnTestQ.setIcon(QIcon(QPixmap(":/Resources/icons/icons/con.png")))
            self.MainToolBar.addWidget(mbtnTestQ)
    
            btn = self.window.findChild(QPushButton, 'pushButton')
            btn.clicked.connect(self.ok_handler)
    
            # self.window.showMaximized()
            self.window.show()
    
    
        def ok_handler(self):
            mbtnTest = QToolButton()
            mbtnTest.setText("Test button")
            self.MainToolBar.addWidget(mbtnTest)
            self.MainToolBar.addWidget(QPushButton('AAA'))
            self.MainToolBar.addWidget(QPushButton('BBB'))
            self.MainToolBar.addWidget(QPushButton('CCC'))
            self.MainToolBar.addWidget(QPushButton('DDD'))
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        form = Form('mainwindow.ui')
        sys.exit(app.exec_())
    

    Also, if I employ the same code to dynamically add toolbar stuff in the ok_handler function, it doesn't add the QPushButtons at all, even though I know the function is triggred, because other actions from the functions work. I removed some code for simplicity, so don't mind it wouldn't work as is shown now.


  • Qt Champions 2019

    What's your path/prefix in your qrc file? See https://doc.qt.io/qt-5/resources.html



  • @Christian-Ehrlicher said in Icons from Resources not showing up in QPushButton or QToolButton:

    What's your path/prefix in your qrc file? See https://doc.qt.io/qt-5/resources.html

    As shown in the tree, the location is

    ./Resources/icons.qrc
    

    As noted, the prefix is:

    /icons
    

    The few lines in the documentation doesn't bring me any new insight, I followed this logic, as well as I followed another logic with copying the path and as well as I tried couple of other things as shown. It probably something very simple I'm not doing right...


  • Lifetime Qt Champion

    Hi,

    Did you run pyrcc to generate the appropriate related code ?



  • @SGaist said in Icons from Resources not showing up in QPushButton or QToolButton:

    Hi,

    Did you run pyrcc to generate the appropriate related code ?

    Nope, I did not run pyrcc, thank you for bringing this up, it's first time I hear of it. Neither the documentaion linked above or other tutorials mentioned it. From what I googled now it's a resource compiler and I would asume it would run together or before hand of the standard compiler.

    Having said that, I do remember that one video tutorial mentioned, that first time after resources are set up, one have to run the project in a different way to get resources working (so not using the the green Play button, but from the menu). However I can't find anythig in the menu that would make sense (like CTRL+SHIFT+B - Compile All, that is greyed out).

    An advice how to compile it from within QtCreator, or a link to tutorial or a dedicated documentation would be very appreciated.


  • Lifetime Qt Champion

    The porting guide tutorial chapter 3 talks about rcc. Note that the name of the tool is pyside2-rcc for PySide2.



  • Heya,
    I usually put this snippet in my resources location to update my resource file when im changing it alot:

    import subprocess
    from pathlib import Path
    
    file_path = Path(__file__)
    output_file = file_path.parent.parent / Path('guiResources.py')
    resource_file = file_path.parent / Path('resources.qrc')
    # cmd and sub process to generated new font resource file
    cmd = f'pyside2-rcc {resource_file} -o {output_file}'
    subprocess.call(cmd, shell=True)
    

    my resource file will look something like this:

    <RCC>
    	<qresource>
    		<file alias="sub_task_checked_16">icons/sub_task_checked_16.png</file>
    		<file alias="progress_bar_1">icons/progress_bar/progress_bar_1.png</file>
    
    	</qresource>
    	<qresource prefix="font">
        	<file alias="Bebas">fonts/Bebas-Regular.ttf</file>
            </qresource>
        </qresource>
    </RCC>
    

    this in my py file i call access an image with:

    QtGui.QPixmap(':/sub_task_checked_16')
    


  • @SGaist Oh, I see the trick. Good to know. I think there's also another similar way - to set a "step" in the project settings in the execution of the program and define the comand there (so that it is executed before the python code itself).


    I converted the icons.qrc file using pyside2-rcc and it went all right, with no messages, producing icons.py, which sits in the root of the project now. It's content looks jason-like (probably should).

    I then imported the file in my header:

    import icons
    

    However, there's no change, I get no icons.

    I deleted the both the icons.py and icons.qrc and created a new resource file iconsres.qrc with all the icons from scratch (using QtCrator GUI), this time in the root (not in the Resource folder). It should have no effect, because the icons.py was in the root anyway. I converted it to iconsres.py and changed the import line to

    import iconsres
    

    The icons are left like:

            self.MainToolBar.addWidget(QPushButton(QIcon(':/icons/AddRecord.png'),'NNN'))
            self.MainToolBar.addWidget(QPushButton(QIcon(':/icons/gear.png'),'OOO'))
            self.MainToolBar.addWidget(QPushButton(QIcon(':/person.png'),'PPP'))
            self.MainToolBar.addWidget(QPushButton(QIcon(':/tools.png'),'RRR'))
    
            mbtnTestQ = QPushButton()
            mbtnTestQ.setText("Qqqqq")
            mbtnTestQ.setIconSize(QSize(24,24))
            mbtnTestQ.setIcon(QIcon(QPixmap(":/icons/co.png")))
            self.MainToolBar.addWidget(mbtnTestQ)
    

    No change whatsoever :-(.



  • did you name your resource like this?:

    <qresource prefix="icons">
    <file alias="AddRecord">icons/AddRecord.png</file>
    </qresource>
    

    maybe get rid of the .png extention

    QtGui.QPixmap(':/icons/AddRecord')
    


  • @alom Thank you for you suggestion. I tried omitting extension, it didn't work.

    My resource file looks exactly like that, except that prefix is starting with a slash and whole content is enclosed in <RCC> tag. I tried to recreate the resource from scratch and omit the slash in prefix, which I left there the first time, but it has no effect - the slash is added automatically.

    <RCC>
        <qresource prefix="/icons">
            <file>Resources/AddRecord.png</file>
            <file>Resources/admin.png</file>
            <file>Resources/envelope.png</file>
            <file>Resources/gear.png</file>
            <file>Resources/helpLarge.png</file>
            <file>Resources/itemRev.png</file>
            <file>Resources/person.png</file>
            <file>Resources/plan.png</file>
            <file>Resources/Table_gray.png</file>
            <file>Resources/Table_green.png</file>
            <file>Resources/Table.png</file>
            <file>Resources/TableEdit.png</file>
            <file>Resources/TableEditCancel.png</file>
            <file>Resources/tagBlue.png</file>
            <file>Resources/tagGreen.png</file>
            <file>Resources/tagRed.png</file>
            <file>Resources/tools.png</file>
        </qresource>
    </RCC>
    

    The converted file (iconsres.py) starts like this:

    from PySide2 import QtCore
    
    qt_resource_data = b"\
    \x00\x00\x01\x8f\
    \x89\
    PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\....................."
    ...
    ...
    ...
    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()
    

    I even tried to add an extra new toolbar elsewhere, but id didn't help:

            # Create pyqt toolbar 
            toolBar2 = QToolBar()
            self.Lout = self.window.findChild(QLayout, 'HomeLayout')
            self.Lout.addWidget(toolBar2)
    
            # Add buttons to toolbar
            toolButton1 = QToolButton()
            toolButton1.setText("Apple")
            toolButton1.setIcon(QIcon(QPixmap(":/icons/plan.png")))
            toolButton1.setCheckable(True)
            toolButton1.setAutoExclusive(True)
            toolBar2.addWidget(toolButton1)
            toolButton2 = QToolButton()
            toolButton2.setText("Orange")
            toolButton2.setIcon(QIcon(QPixmap(":/icons/tools.png")))
            toolButton2.setCheckable(True)
            toolButton2.setAutoExclusive(True)
            toolBar2.addWidget(toolButton2)
    

    alt text

    The "Apple" and "Orange" are the new buttons of a new toolBar2. Can it be something with the size of the buttons? Setting icon size explicitly doesn't help:

        toolBar2.iconSize = QSize(24, 24)
    

    Setting toolbar height doesn't seem to have visual effect:

        toolBar2.height = 60
    

    Just to be sure, I'll create a new project and set everything from scratch, but if anyone has any ideas, I'd appreciate them.



  • I've noticed, that @alom 's example has aliases set for the pictures, so I tried to set the aliases to all used pictures, then use reference without the ".png" extension as per aliases and recompile the resource file. It has no effect either.



  • Heya,
    Hmmm... strange, i'm not sure where the issues is. If you not using an alias and only prefix, I would have though the file path should be:

    ":/icons/Resources/TableEdit.png"
    

    Thats why I use prefix and alias in my qrc file, so my path is always:

    ":/prefix/alias"
    

    and if you just explicitly set the image path for a test, your the image is defiantly working fine?

    toolButton1.setIcon(QIcon(QPixmap("your/local/network/path/plan.png")))
    


  • @alom Well, I would distinguish the file path in the XML file and resource reference in the code. When the inconsres.qrc is compiled into iconsres.py, it doesn't matter what paths are in the inconsres.qrc, as long as the iconsres.py contains the converted binary data (which in my case it appears it does, it looks similar to images when encoded to jason).
    So then it's important that the reference from the code is correct to the encoded images. Something might be broken within the encoded iconsres.py file, the reference in QPixMap might be incorrect or something in the GUI object might be set wrong.

    Is my line of thinking correct?

    Also, do I understand it right, that there are no tools to track variables and objects through execution? I mean something functionally similar i.e. to VS, like mouse hovering over included iconsres.py to see, that it contains objects like "icons/person.png", etc.



  • To be honest i'm not sure, but trying to recreate your example above in a test file at home, where I did not not use an aslias, I had to use

    ":/icons/Resources/TableEdit.png"
    

    to get the image to show. Not including 'Resources" in the path failed to load it.

    Unfortunately this is the extent of my knowledge with resource files



  • @alom Oh, that is interesting and very helpful, thank you! It doesn't work for me, though. Could you tell me please, did you do the conversion using:

    pyside2-rcc iconsres.qrc -o iconsres.py
    

    ?



  • @alom I mised your last comment about the direct link to image file. I tested it now and yes, that works well.



  • @Oak77

    Heya,
    yes I used the snippet I posted in my above post. I save this as a separate .py file where i keep my resources and run this to generate my files. You need to make sure your system path env has acess to pyside2-rcc, else you need to modify the code to point to the pyside2-rcc on your network.

    import subprocess
    from pathlib import Path
    
    file_path = Path(__file__)
    output_file = file_path.parent.parent / Path('guiResources.py')
    resource_file = file_path.parent / Path('resources.qrc')
    # cmd and sub process to generated new font resource file
    cmd = f'pyside2-rcc {resource_file} -o {output_file}'
    subprocess.call(cmd, shell=True)
    


  • @alom I still can't make it work. Your way is exactly how I do it, but for the moment I run the command directly as noted above.

    I did another test with QLabel:
    Images from resources

    The imgs.qrc is shown in the screenshot including it's content, one picture (qtlogo.png). Converted this way:

    pyside2-rcc imgs.qrc -o imgs.py
    

    Code fractions:

    from PySide2.QtWidgets import ..., QToolButton, ..., QLabel
    from PySide2.QtGui import QIcon, QPixmap
    import imgs
    ...
            self.pic1box = self.window.findChild(QLabel, "Pic1")
            self.pic1box.setText = "/home/../Test8/qtlogo.png"
            self.pic1box.setPixmap(QPixmap("/home/libor/Coding/Projects/Test8/qtlogo.png"))
    
            self.pic2box = self.window.findChild(QLabel, "Pic2")
            self.pic2box.setText = ":/img/qtlogo.png"
            self.pic2box.setPixmap(QPixmap(":/img/qtlogo.png"))
    
            self.pic3box = self.window.findChild(QLabel, "Pic3")
            self.pic3box.setText = ":/qtlogo.png"
            self.pic3box.setPixmap(QPixmap(":/qtlogo.png"))
    
            self.pic4box = self.window.findChild(QLabel, "Pic4")
            self.pic4box.setText = ":/img/qtlogo"
            self.pic4box.setPixmap(QPixmap(":/img/qtlogo"))
    
            self.pic5box = self.window.findChild(QLabel, "Pic5")
            self.pic5box.setText = ":/qtlogo"
            self.pic5box.setPixmap(QPixmap(":/qtlogo"))
    
            self.pic6box = self.window.findChild(QLabel, "Pic6")
            self.pic6box.setText = ":/Resources/img/qtlogo.png"
            self.pic6box.setPixmap(QPixmap(":/qtlogo.png"))
    

    All icons and images shown have the path set directly, not via resource file.


  • Lifetime Qt Champion

    What version of PySide2 are you using ?



  • @SGaist I don't know if this is a proper way to find out, but versions are listed:

    > pip install --upgrade PySide2
    Defaulting to user installation because normal site-packages is not writeable
    Requirement already up-to-date: PySide2 in /usr/lib64/python3.6/site-packages (5.15.0a1.dev1584103015)
    Requirement already satisfied, skipping upgrade: shiboken2==5.15.0a1.dev1584103015 in /usr/lib64/python3.6/site-packages (from PySide2) (5.15.0a1.dev1584103015)
    
    


  • OK, there are example apps that come with PySide2. Now I went into this one, which is using resource file:

    /usr/lib64/python3.6/site-packages/PySide2/examples/widgets/animation/appchooser/
    

    ...and run it:

    > python3 ./appchooser.py
    QPixmap::scaled: Pixmap is a null pixmap
    QPixmap::scaled: Pixmap is a null pixmap
    QPixmap::scaled: Pixmap is a null pixmap
    QPixmap::scaled: Pixmap is a null pixmap
    QPixmap::scaled: Pixmap is a null pixmap
    QPixmap::scaled: Pixmap is a null pixmap
    QPixmap::scaled: Pixmap is a null pixmap
    QPixmap::scaled: Pixmap is a null pixmap
    

    and I see only white blank window. Could someone try to run this example app and let me know it it is working? I wonder, if it's connected to this issue I'm experiencing with resource files.



  • I somehow progressed a bit with the issue. I tried another Python, the virtual environment that I set during some of tutorials. I installed a PySide2 into that venv and when running my test application in that environment, I can see 2 more pictures in QLabels (actualy those which should work).

    alt text

    But what is interesting nad unfortunate, the icons on the QPushButtons still don't work (the 2 that are shown are assigned via direct link, not a resource file). Icons are in different resource file (iconsres.qrc/icons) from the images (imgs.qrc/img), as is visible in the tree depicted in the screenshot.

    Also, when I ran the AppChooser example from PySide2 example set, it was working with the venv.

    So I did "pip uninstall pyside2" and "pip uninstall shoboken2" and then "pip install shoboken2" and "pip install pyside2", but it didn't help.

    I have two conclusions:

    1. Something is broken in my system Python. I have no idea how to fix it.
    2. There's something wrong with the iconsres.qrc and also with new testres.qrc, third resource I tried to create in the test project. The 2nd resource file imgs.qrc is valid. But I don't know what went wrong, what's the difference between them.

    Any advices for further steps appreciated.


  • Lifetime Qt Champion

    Can you provide a minimal example with all files that allows to reproduce your issue ?


  • Banned

    Well Oak and I got this working in PyQt5 over in the classroom but to do so the resource came from a resource .py file that was created with a tool and you had to use QPixMap(...) on the resource file data to add it to the Label and use a QIcon(QPixMap(...)) on the resource file item to put it in the Button -- of course I am not sure if that works in PySide2 as I have no way to test that.



  • @Denni-0 Yep, the thing is, that your modified PyQt5 code works on my system Python, but does not work on the virtual environment Python! So the exact oposite of the case of PySide2 version... It gets even more confusing. Obviously on both is installed the same PyQt5 and PySide2 package.


  • Banned

    @Oak77 right but to me that drills down to the environment not the code implementation -- aka whatever or why-ever it is not working has to do with the environment it is in since I can run it just fine and you can in one of your environments run it just fine.



  • @SGaist Yep, I created this one. Test12.zip


  • Lifetime Qt Champion

    Since you gave an alias to each entry of the qrc file, use that allias to access the images.

    For example:

    self.pic1box.setPixmap(QPixmap(":/icons/qtlogo"))
    


  • @SGaist Tested, wierd results gets wierder. There should be no difference between usage of alias and file name. But guess what - it still doesn't work in my system environment Python, but in the virtual environment Python, it works with aliases, but doesn't with file names!
    Just look at the last screenshot (some 5 posts above), where it works both with the alias and the file name, as it should (it's two unscaled QtLogo images on the right).

    Now it looks like this: On the left is app run in system environment or in venv using file names and on the right using virtual Python environment with aliases:
    screenshots minimal

    By now I know it's not really the code, but this uncertainty and instability is not a hair smaller concern anyway. One cannot deliver reliable app, not knowing what's happening.

    I believe it has to be in the Python itself. There must be some difference between the package distributed in OpenSUSE:

    > python3 --version
    Python 3.6.10
    > pip show PySide2
    Name: PySide2
    Version: 5.14.1
    Summary: Python bindings for the Qt cross-platform application and UI framework
    Home-page: https://www.pyside.org
    Author: Qt for Python Team
    Author-email: pyside@qt-project.org
    License: LGPL
    Location: /usr/lib64/python3.6/site-packages
    Requires: shiboken2
    

    ...and the virtual environment:

    > ~/Coding/Projects/venv/bin/python3 --version
    Python 3.6.10
    > ~/Coding/Projects/venv/bin/pip3 show PySide2
    Name: PySide2
    Version: 5.14.1
    Summary: Python bindings for the Qt cross-platform application and UI framework
    Home-page: https://www.pyside.org
    Author: Qt for Python Team
    Author-email: pyside@qt-project.org
    License: LGPL
    Location: ~/Coding/Projects/venv/lib/python3.6/site-packages
    Requires: shiboken2
    

    They look same, but there must be a glitch somewhere.


  • Lifetime Qt Champion

    Sill question but, did you install both using pip ?



  • @SGaist The system environment Python is installed via YaST from official OpenSuSE repository. As for virtual environment, I already forgot how I did it (it was according to tutorial), but I think it was like "python3 -m venv venv-name" (so actually based on the system one). From there, everything was done like:

    pip install Shoboken2
    pip install PySide2
    

    When trying to update:

    pip install --upgrade Shoboken2
    pip install --upgrade PySide2
    

    ...it says they're up-to-date.

    I uninstalled Python from my system and then installed it back (the same way via YaST) and then uninstalled both Shoboken2 and PySide2, but nothing helped.



  • OK, I found a cause. Not the root cause, but at least how to "replicate" this issue.

    I found out, that apart from PySide2 installation using pip command, a totally different intallation is coexisting in my system, which I can see in my YaST software manager, named "Python3-PySide2". When I remove it, the resource files starts to work in my system environment Python.

    Interestingly enough, pip3 install/uninstall PySide2 command works without even noticing that system PySide2 package. And if it's removed by pip uninstall, PySide2 programs would claim there's no PySide2 installed. When installed back using pip3 install pyside2, they wouldn't run properly with resource files.

    The bad new for me is, that I can't remove this package, because it then removes also some shiboken2-cpython libraries, that are essential to start a FreeCAD. I need this software, so I can't remove the system PySide2. And I don't know what exactly is broken there, so I can't fix it. I might ask in OpenSUSE forums.

    One more note - the programs look differently when the system PySide2 package is/is-not inistalled. Like another style sheet is used to render them (dark frames vs. light, small paddings in buttons and textboxes vs. large, etcc).

    For the time being, I will have to use virtual environment Python.



  • if possible in you working evn, I have had really nice result using conda instead of pip. It manages all the dependencies out side of python for you. You can quite easily create any combination of python version and qt5/pyside2, plus any other python libs you need for your project. Pycharm even comes with a Anaconda installer, but I keep my separated.

    Maybe that can help you manage you envs


Log in to reply