Search code examples
pythonqtpysideqtstylesheets

Modifying widget colour at runtime without overwriting stylesheet


My situation: I have a widget that has a stylesheet set. That stylesheet may or may not include colour settings. I want to change the colour of the widget, but I can't just do widget.setStyleSheet("QWidget {background-color: %s}"% colour), because that replaces the existing stylesheet, which I don't want to do.

My Question: What is the correct method for changing the (background, in my case) colour of a widget without erasing that widget's stylesheet? Is there a better method than parsing and appending to the style sheet?

Example:

In the code below, how can I change the colour of box (imagining that the box's colour must change dynamically; e.g., the box is red when it contains an even number of items, and green when the number is odd)?

import sys
from PySide import QtGui, QtCore

class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def initUI(self):
        box = QtGui.QComboBox(self)
        box.resize(box.sizeHint())
        box.setStyleSheet("""
QComboBox::drop-down {border-width: 0px;}
QComboBox::down-arrow {image: url(noimg); border-width: 0px;}
""")
        box.move(50, 50)

        #Using the palette doesn't work:
        pal = box.palette()
        pal.setColor(box.backgroundRole(), QtCore.Qt.red)
        box.setAutoFillBackground(True)
        box.setPalette(pal)

        self.setGeometry(300, 300, 250, 150)
        self.show()


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

Using the box's pallet doesn't work, presumably as per this warning on the autoFillBackground method:

Warning: Use this property with caution in conjunction with Qt Style Sheets. When a widget has a style sheet with a valid background or a border-image, this property is automatically disabled.


Solution

  • You can use dynamic properties to do this:

    from PySide import QtCore, QtGui
    
    class Window(QtGui.QWidget):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            self.edit = QtGui.QLineEdit(self)
            self.edit.setProperty('warning', False)
            self.edit.setStyleSheet("""
               /* other rules go here */
               QLineEdit[warning="true"] {background-color: yellow};
               QLineEdit[warning="false"] {background-color: palette(base)};
                """)
            self.button = QtGui.QPushButton('Test', self)
            self.button.clicked.connect(self.handleButton)
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.edit)
            layout.addWidget(self.button)
    
        def handleButton(self):
            self.edit.setProperty(
                'warning', not self.edit.property('warning'))
            self.edit.style().unpolish(self.edit)
            self.edit.style().polish(self.edit)
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.show()
        sys.exit(app.exec_())