HyunYoung Go
HyunYoung Go

Reputation: 103

PySide6 QLabel padding not applied when unpolish, polished

I define style sheet and test.py like below.

I expected the label's text padding change 10px -> 100px but its not applied. The background color changed to RED. I test color, font-size as well, but only 'padding' didn't applied after click the button.

Can anyone explain why only padding not applied in style sheet?

style

.QLabel[text="OK"]{
  background-color: rgb(0,255,0);
    color: rgb(255,250,250);
    font-size: 24pt;
    padding: 10px;
}

.QLabel[text="ERR"]{
  background-color: rgb(255,0,0);
    color: rgb(255,250,250);
    font-size: 24pt;
    padding: 100px;
}

test.py

from PySide6 import QtWidgets
from PySide6.QtCore import Qt


class MyWindow(QtWidgets.QWidget):
    def set_style(self):
        with open('./style', 'r') as f:
            self.setStyleSheet(f.read())

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

        self.set_style()
        self.setupUi()

    def setupUi(self):
        self.label = QtWidgets.QLabel('OK', self)
        self.label.setAlignment(Qt.AlignCenter)
        self.button = QtWidgets.QPushButton('Button', self)

        vlayout = QtWidgets.QVBoxLayout(self)
        vlayout.setAlignment(Qt.AlignCenter)
        vlayout.addWidget(self.label)
        vlayout.addWidget(self.button)

        self.button.clicked.connect(self.btn_clicked)

        self.show()

    def btn_clicked(self, a):
        self.label.setText("ERR")
        self.label.style().unpolish(self.label)
        self.label.style().polish(self.label)
        self.update()


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    win = MyWindow()
    app.exec_()

before btn click after btn click

Upvotes: 1

Views: 618

Answers (1)

musicamante
musicamante

Reputation: 48231

tl;dr

Just call self.setStyleSheet(self.styleSheet()) to ensure that the style is actually reapplied.

Explanation

While in some parts of the documentation (and some QSS related questions) there are references to polish() and unpolish(), those functions are not always guaranteed to "do everything".

The documentation partially explains what polish() does:

Initializes the appearance of the given widget.
[...]
Note that the default implementation does nothing. Reasonable actions in this function might be to call the QWidget::setBackgroundMode() function for the widget. Do not use the function to set, for example, the geometry.

In fact, what those two functions (which are virtual[1]) do is to, possibly, set something and restore it back, or the other way around. But that might not be sufficient. They could even do nothing at all.

Without going too deep into the implementation, suffices to say that [un]polishing is usually insufficient for the box model updates that are not directly related to the contents (like the font), and this is especially important for property selectors, as the documentation explains:

Warning: If the value of the Qt property changes after the style sheet has been set, it might be necessary to force a style sheet recomputation. One way to achieve this is to unset the style sheet and set it again.

So, the easiest solution is just that: re-set the stylesheet.

In this case, the following will be enough:

    def btn_clicked(self, a):
        self.label.setText("ERR")
        self.setStyleSheet(self.styleSheet())

Notes:

  • calling setStyleSheet() with the current stylesheet is acceptable, as it's always guaranteed that the style will be computed again as long as the widget already uses a stylesheet, even indirectly; due to the cascading nature of QSS (as for CSS), this obviously possibly affects any child widget;
  • calling self.update() (on the parent) is normally useless, as it will only schedule a repaint on that parent, so:
    • a scheduled repaint only calls paintEvent() again, and will normally have no effect on "changed" stylesheet, and that's because a paint function only cares about painting and should never call for geometry changes;
    • the repaint usually happens only on the widget for which it was scheduled: in most cases, children widgets will completely ignore it (so they will not be repainted);
  • as a general rule, it's usually suggested to avoid to use QSS property selectors for properties that might change at runtime; while, as explained above and in the documentation, this can be worked around, it's still unsafe and usually discouraged; always use this feature with care and after deep consideration of the possible alternatives;

[1] With C++, "virtuals" are functions that can be overridden by a subclass and that can be called internally by Qt; this results in various aspects: 1. non-virtual functions can only be explicitly called from PyQt/PySide, so you could theoretically use any name you want, as it won't affect the default behavior; 2. some non-virtual functions are explicitly called by PyQt when using the uic module, so it's partially safe to override them for the reason above; 3. abstract classes might require virtuals to be overridden in order to be used (i.e., the data() method of QAbstractItemModel); 4. the base implementation might do nothing, and the virtual is provided for convenience (the tabInserted() of QTabBar); 5. the base implementation might do something, and you may need to reimplement "that something" or at least call the base implementation (the mouse/key events of QWidget);

Upvotes: 2

Related Questions