1

I'm trying to change the current color group fora QPalette, but it seems that the setCurrentColorGroup method of QPalette simply does not work.

I'm running this code:

app = QtGui.QApplication(sys.argv)

button = QPushButton()
svgWidget = QSvgWidget(resources_paths.getPathToIconFile("_playableLabels/42-labelPlay-disabled-c.svg"))

button.setLayout(QHBoxLayout())
button.layout().addWidget(svgWidget)

button.setFixedSize(QSize(300, 300))

print button.palette().currentColorGroup()
button.setEnabled(False)
print button.palette().currentColorGroup()
button.palette().setCurrentColorGroup(QPalette.ColorGroup.Normal)
print button.palette().currentColorGroup()
button.show()
print button.palette().currentColorGroup()

app.exec_()

This is the output I get:

PySide.QtGui.QPalette.ColorGroup.Normal
PySide.QtGui.QPalette.ColorGroup.Disabled
PySide.QtGui.QPalette.ColorGroup.Disabled
PySide.QtGui.QPalette.ColorGroup.Disabled

Process finished with exit code -1

So... It seems that setCurrentColorGroup does exactly nothing. Any ideas on how could I change the current color group?

Thanks in advance!

(BTW, I'm running PySide 1.2.4 with Qt 4.8 on a Windows 7 system)

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
Guillermo Ares
  • 197
  • 1
  • 12
  • The `palette()` method is `const &` - i.e. in python terms, it returns a copy, not a reference. So you need to modify the returned palette, then use `button.setPalette(palette)` to apply the changes. However, using `setCurrentColorGroup` is still unlikely to have any effect, since the button will still be disabled. Also, you need to be aware that some styles don't use the palette for all types drawing (this mostly affects certain versions of Windows and OSX). Maybe you should explain what you trying to achieve by using palette in this way. – ekhumoro Jan 11 '17 at 20:16
  • @ekhumoro I got a button (a flat and borderless subclass of QPushButton) which uses an svg as its icon. The source svg has a red background which turns into gray when I disable the button. I don't want the icon colors to be distorted. I don't know exactly how the button alters the colors when disabled, but I figured out it should have something to do with the palette, so I tried setting the currentColorGroup of the palette back to Normal (instead of Disabled), but it does nothing... – Guillermo Ares Jan 12 '17 at 15:32
  • @ekhumoro I'm using PySide, so I have no such things as const variables or pointers. I verified that every time I call button.palette() I'm getting the same instance of QPalette, but may be that's just a wrapper? I'll give it a try using setPalette() as you suggested. – Guillermo Ares Jan 12 '17 at 15:34
  • @ekhumoro It doesn't work either... As a matter of fact, creating a QPalette and then using setCurrentColorGroup on it does nothing either. I find this really wierd :/ – Guillermo Ares Jan 12 '17 at 15:38
  • No, you're using Qt via Python, so the C++ signatures are always relevant. If you don't understand this crucial point, you'll never be able to use PySide/PyQt effectively. In Python, modifying a copy of a mutable object (such as a list), *will not modify the original* - and C++ is no different in that regard. So if a Qt method returns a `const&`, then, in PySide, you get a copy; but if it returns a pointer, you get the original. PySide is just a thin wrapper around a C++ library. Calling a PySide method always calls *exactly the same method* in Qt. – ekhumoro Jan 12 '17 at 18:32
  • Last time I tried to test what you said by doing `print id(button.palette())` twice. I got the same id every time. Now I noticed that python was just reusing the last object id immediately after destroying the first palette... I just tried storing the palettes before comparing ids and they were different. Anyway, I tried using setPalette as you told me and it didn't work. Even creating a palette with `QPalette()` and then setting the current color group with `setCurrentColorGroup(QPalette.Active)` did nothing at all! `palette.currentColorGroup()` keeps returning `QPalette.Normal`. – Guillermo Ares Jan 16 '17 at 15:46
  • It does work - it's just that you have misunderstood what `setCurrentColorGroup` does. It's just a convenience method that allows you to set the group before changing the colors of several roles. Obviously, If you don't explicitly change any colors, nothing interesting will happen. But in any case, as I explained in my answer, the palette has nothing to do with rendering images, so none of this is really relevant to your actual problem. – ekhumoro Jan 16 '17 at 18:12

1 Answers1

2

It seems that you are trying to change the way icons are rendered, rather than the way widgets are painted, so the palette is not the right API to use. Instead, you should use a QIcon, which allows different images to be used for various modes and states.

To use the same image for both Normal and Disabled modes, you would use code like this:

icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap('image.svg'), QtGui.QIcon.Normal)
icon.addPixmap(QtGui.QPixmap('image.svg'), QtGui.QIcon.Disabled)
button = QtGui.QPushButton()
button.setIcon(icon)

However, you should take careful note of this warning from the Qt docs:

Custom icon engines are free to ignore additionally added pixmaps.

So there is no guarantee that this will work with all widget styles on all platforms.

UPDATE:

If the above method doesn't work, it probably means the widget style is controlling how disabled icons are rendered. The relevant QStyle API is generatedIconPixmap, which returns a copy of the pixmap modified according to the icon mode and style options. It seems that this method may sometimes also take the palette into account (somewhat contrary to what I stated above) - but when I tested this, it did not have any affect. I reset the palette like this:

palette = self.button.palette()
palette.setCurrentColorGroup(QtGui.QPalette.Normal)
palette.setColorGroup(QtGui.QPalette.Disabled,
    palette.windowText(), palette.button(),
    palette.light(), palette.dark(), palette.mid(),
    palette.text(), palette.brightText(),
    palette.base(), palette.window(),
    )
button.setPalette(palette)

which made the colours look normal when the button was disabled - but the icon was still greyed out. Still, you might want to try it in case things work differently on your platform (don't be surprised if they don't).

It seems that the correct way to control the disabling of icons is to create a QProxyStyle and override the generatedIconPixmap method. Unfortunately, this class is not available in PyQt4, but I have tested it in PyQt5, and it works. So the only working solution I have at the moment is to upgrade to PyQt5, and use QProxyStyle. Here is a demo script that shows how to implement it:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class ProxyStyle(QtWidgets.QProxyStyle):
    def generatedIconPixmap(self, mode, pixmap, option):
        if mode == QtGui.QIcon.Disabled:
            mode = QtGui.QIcon.Normal
        return super(ProxyStyle, self).generatedIconPixmap(
            mode, pixmap, option)

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QtWidgets.QPushButton(self)
        self.button.setIcon(QtGui.QIcon('image.svg'))
        self.button2 = QtWidgets.QPushButton('Test', self)
        self.button2.setCheckable(True)
        self.button2.clicked.connect(self.button.setDisabled)    
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.button)
        layout.addWidget(self.button2)

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(ProxyStyle())
    window = Window()
    window.setGeometry(600, 100, 300, 200)
    window.show()
    sys.exit(app.exec_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • Hi again! I'm using a QIcon exactly this way: `cls.PlayEnabledIcon = QIcon(resources_paths.getPathToIconFile("_playableLabels/42-labelPlay-enabled.svg")) cls.PlayEnabledIcon.addFile(resources_paths.getPathToIconFile("_playableLabels/42-labelPlay-disabled-c.svg"), mode=QIcon.Disabled)` I still get my red icon rendered gray. – Guillermo Ares Jan 16 '17 at 15:48
  • @GuillermoAres. As I said: "there is no guarantee that this will work with all widget styles on all platforms". I've added a little more information to my answer that suggests a possible work-around. – ekhumoro Jan 16 '17 at 18:30
  • I read that, but I know for a fact that it is not ignoring the file I'm adding for the `QIcon.Disabled` mode since it is quite different from the one I'm setting for `QIcon.Normal` (first's a "play" triangle and the other one is an exclamation mark). It does show the exclamation mark when disabled, but it changes the color of the question mark from red to gray. I've seen Qt change the color of icons automatically for disabled buttons, that's why I thought it could have something to do with the palette but may be it's the engine itself. Too obscure for me. Thanks for the patience! :) – Guillermo Ares Jan 16 '17 at 19:50
  • Btw, I don't really know what to do with this question. It got a little messy. Since you seem to have more experience than me in such affairs... Should I mark your answer as correct? – Guillermo Ares Jan 16 '17 at 19:53