Search code examples
pythonqtmayapyside2

Qt Dynamic Properties - Works for widget if not parented only?


I'm trying to achieve a button behavior on a normal QWidget and I'm trying to use setProperty() to dynamically change values in the stylesheet. It seems to works well when I show the widget using its show() method but once it is parented It does not works anymore, I don't understand what happen ?? (No, I don't want to inherit from QPushButton, the widget will be a lot different, thanks.)

There is an example:

from PySide2 import QtCore, QtGui, QtWidgets


class Example(QtWidgets.QWidget):
    
    STYLESHEET = \
    """
    QWidget[pressed=true] 
    {
        background-color: yellow;
    }
    
    QWidget[pressed=false] 
    {
        background-color: red;
    }
    """
    
    pressed = QtCore.Signal()
    
    def __init__(self, parent=None):
        super(Example, self).__init__(parent=parent)
        
        self._buildUi()

        self.setStyleSheet(self.STYLESHEET)

        self.setProperty('pressed', False)
        
    def _buildUi(self):
        self.layout = QtWidgets.QVBoxLayout(self)
        
        self.label = QtWidgets.QLabel('toto')
        self.layout.addWidget(self.label)

    def mousePressEvent(self, e):
        self.pressed.emit()
        self.setProperty('pressed', True)
        self.setStyle(self.style())
                
        super(Example, self).mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        self.setProperty('pressed', False)
        self.setStyle(self.style())
        
        super(Example, self).mouseReleaseEvent(e)
        
        
class Window(QtWidgets.QWidget):
    
    def __init__(self):
        super(Window, self).__init__()
        
        self.mainLayout = QtWidgets.QVBoxLayout(self)
                
        self.test = Example(self)
        self.mainLayout.addWidget(self.test)


if __name__ == '__main__':
    # w = Example()  # Work
    w = Window()  # Don't Work
    w.show()

If you switch the commented lines in the if __name__ == '__main__': condition you will be able to see both result of the same widget.

I tried to:

  • call widget mousePressEvent and mouseReleaseEvent from parent (just to test) - Don't work
  • Send a StyleChange event manually to the QApplication - Don't work
  • unpolish/polish manually - Don't work
  • set the stylesheet again - Dont' work
  • Inherit from QPushButton - Work, but not what I want (I need to understand how to make this work, not use the allready existing properties.)

PS: I run this code inside Autodesk Maya, so the QApplication instance allready exists as it is Maya itself.

Do you guys know what happen ?


Solution

  • The standalone widget works because Qt uses the basic stylesheet properties (background and border) only for top level windows, assuming that the top level window is of the same class as that specified in the stylesheet.

    When a QWidget has a parent, it does not paint anything on its own.
    As the documentation explains:

    If you subclass from QWidget, you need to provide a paintEvent for your custom QWidget

    So, in PyQt/PySide terms:

    class Example(QtWidgets.QWidget):
        # ...
        def paintEvent(self, event):
            opt = QtWidgets.QStyleOption()
            opt.initFrom(self)
            qp = QtGui.QPainter(self)
            self.style().drawPrimitive(
                QtWidgets.QStyle.PE_Widget, opt, qp, self)