Issue
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_()
Solution
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);
- a scheduled repaint only calls
- 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);
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.