+ PYSIDE STYLESHEET AND CUSTOM PAINTING

How to use stylesheets with custom drawing in PySide2

I am working on a custom widget to create a collapsable frame, like the one used in the Maya attribute editor. In this widget I have a label and an arrow to indicate the expanded/collapsed state of the widget. I wanted to learn how to implement a stylesheet to be able to set a few visual properties of the widget. Some of the properties comes for free like the background-color, border-radius, font-size and color etc. But I also wanted to be able to set the color of the arrow. The image below is an example of a simplified version of the widget.

stylesheet and custom drawing

In the article “Qt Style Sheets and Custom Painting Example” from the Qt docs if found a C++ example of how to do this. After some further googling and some trial and error I got it to work with PySide2.

Below is a simplified example of the widgets.

class DotLabel(QtWidgets.QFrame):

    def __init__(self, name, height=20, parent=None):
        super(DotLabel, self).__init__(parent)

        self._dot_color = QtGui.QColor(0, 0, 0)
        self._name = name
        self._height = height
        self.setFixedHeight(height)

    def get_dot_color(self):
        return self._dot_color

    def set_dot_color(self, color):
        self._dot_color = color

    def paintEvent(self, e):

        qp = QtGui.QPainter(self)
        qp.setRenderHint(QtGui.QPainter.Antialiasing)
        rect = QtCore.QRect(self._height, 0, self.width(), self._height)
        qp.drawText(rect, QtCore.Qt.AlignVCenter, self._name)
        qp.setBrush(self.get_dot_color())
        qp.setPen(QtCore.Qt.NoPen)
        qp.drawEllipse(QtCore.QPoint(self._height*.5, self._height*.5), self._height*.20, self._height*.20)
        qp.end()

    dotColor = QtCore.Property(QtGui.QColor, get_dot_color, set_dot_color)

And in the “main” widget.

class TestWidget(QtWidgets.QWidget):
    
    def __init__(self, parent=None):
        super(TestWidget, self).__init__(parent)

        self.setGeometry(100,240,400,200)

        vbox = QtWidgets.QVBoxLayout(self)

        # add widgets
        for i in range(3):

            dot = DotLabel('Petfactory {}'.format(i), 20+i*20)
            dot.setObjectName('dot_{}'.format(i))
            vbox.addWidget(dot)

        # apply stylesheet
        s_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'stylesheet.qss')
        with open(s_path, 'r') as f:
            self.setStyleSheet(f.read())

Example from the style sheet:

DotLabel {
    font-family: "Futura";
}

DotLabel#dot_0 {
    border-radius: 2px;
    border: 1px solid rgb(253, 151, 32);
    color: rgb(253, 151, 32);
    font-size: 12px;
    qproperty-dotColor: rgb(253, 151, 32);
}

DotLabel#dot_1 {
    border-radius: 4px;
    color: rgb(166, 226, 46);
    font-size: 24px;
    qproperty-dotColor: rgb(166, 226, 46);
    background-color: rgb(90,90,90);
}

DotLabel#dot_2 {
    border-radius: 6px;
    color: rgb(102, 217, 239);
    font-size: 40px;
    qproperty-dotColor: rgb(102, 217, 239);
    background-color: rgb(90,90,90);
}

While in the research phase I came across a nice post by Dhruv Govil not exactly related by more on dynamic properties and stylesheets. There is also this article from the Qt docs

If you have some feedback or ideas on how to improve this workflow please let me know.