QCSS precedence versus "in-line code"
-
I am "surprised" at the precedence given to QCSS rules over "in-line" code. Consider the following:
import sys from PyQt5 import QtWidgets, QtGui class Main(QtWidgets.QMainWindow): def __init__(self): super().__init__() # set default button minimum height & background color ss = 'QPushButton { min-height: 100px; background-color: red; }' # comment & uncomment next line # QtWidgets.QApplication.instance().setStyleSheet(ss) self.setWindowTitle("Main") self.setGeometry(100, 100, 500, 500) self.centralWidget = QtWidgets.QWidget(self) self.setCentralWidget(self.centralWidget) self.centralLayout = QtWidgets.QHBoxLayout(self.centralWidget) self.btn1 = QtWidgets.QPushButton("Button #1") self.centralLayout.addWidget(self.btn1) self.btn2 = QtWidgets.QPushButton("Button #2") # set explicit minimum height self.btn2.setMinimumHeight(200) # set explicit button background color # (see also my "**" comment at the end of the post) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Button, QtGui.QColor("yellow")) self.btn2.setAutoFillBackground(True) self.btn2.setPalette(pal) self.centralLayout.addWidget(self.btn2) self.show() if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) main = Main() sys.exit(app.exec_())
Compare running this with the
QtWidgets.QApplication.instance().setStyleSheet(ss)
line commented/uncommented.When the style sheet rule is in play, it overrides both the height & color set on
btn2
explicitly.I come from an HTML/CSS background. There we are used to anything set directly on an element (whether via stylesheet or, say, explicit JavaScript code altering attributes) overriding a CSS rule.
So, in Qt, I guess I need to regard anything set via a style sheet, at any level, as overriding any corresponding thing the code may have set on the widget explicitly. Is that right?
Then, to achieve one button different from the default, I have to do it via
setStyleSheet()
on the widget rather than the explicit "in-line code" making Qt widget calls. Is that right?Background: This is problematic for me. I am converting thousands of lines of code over to trying to use a global, external QCSS file, at least for defaults. The behaviour means that I need to find a lot more in the existing code to alter, e.g. I cannot allow any explicit
setMinimumHeight()
calls to remain, because they get overridden if the QCSS has a defultminimum-height
rule. :(** Incidentally: Are you responsible for https://wiki.qt.io/How_to_Change_the_Background_Color_of_QWidget ? Because that is wrong for
QPushButton
, and other people are attempting to use it as it comes first on search forqt5 widget set color
. The code there claims:// set black background pal.setColor(QPalette::Background, Qt::black);
However, it is not obvious that for a
QPushButton
you actually need:// set black background pal.setColor(QPalette::Button, Qt::black);
-
Try adding the button to the layout first, then applying your explicit changes. I suppose what happens now is that you set up your custom widget, and then when you add it to layout, the layout is pushing it's stylesheet to that widget.
-
@JonB said in QCSS precedence versus "in-line code":
So, in Qt, I guess I need to regard anything set via a style sheet, at any level, as overriding any corresponding thing the code may have set on the widget explicitly. Is that right?
No, that is wrong. QSS works only for the widget you set it on + all children. It does not go up in the parent-child tree.
-
@sierdzio said in QCSS precedence versus "in-line code":
@JonB said in QCSS precedence versus "in-line code":
So, in Qt, I guess I need to regard anything set via a style sheet, at any level, as overriding any corresponding thing the code may have set on the widget explicitly. Is that right?
No, that is wrong. QSS works only for the widget you set it on + all children. It does not go up in the parent-child tree.
For this one, I think you're misunderstanding me. I know that inheritance is only downward, not upward (remember, I'm familiar with CSS!). I didn't imply otherwise. I meant: no matter where I do set CSS --- e.g. directly on a widget, or on any level of parentage --- that is going to override any explicit, say,
setMinimumHeight()
calls the code might make directly on the widget, of which there are probably many to track down in the code. And it will apply to any call, not justsetMinimumHeight()
..... -
@sierdzio said in QCSS precedence versus "in-line code":
Try adding the button to the layout first, then applying your explicit changes. I suppose what happens now is that you set up your custom widget, and then when you add it to layout, the layout is pushing it's stylesheet to that widget.
Nope! Done that (moved
self.centralLayout.addWidget(self.btn2)
up to immediately belowself.btn2 = QtWidgets.QPushButton("Button #2")
), behaviour is exactly the same, stylesheet overrides "in-line code" ... ? -
@JonB said in QCSS precedence versus "in-line code":
Nope! Done that (moved self.centralLayout.addWidget(self.btn2) up to immediately below self.btn2 = QtWidgets.QPushButton("Button #2")), behaviour is exactly the same, stylesheet overrides "in-line code" ... ?
Looks like a bug to me, then.
-
@JonB said in QCSS precedence versus "in-line code":
Well, I can't see it's a "bug", as it must be a thorough-going behaviour principle.
If it is not mentioned in the documentation, it is a bug. Besides, the intuition is that when you change something explicitly, it should not be overridden by default global setting, IMO. So it feels buggy to me (esp. since I don't remember it working this way previously).
-
@sierdzio
I take your point. However it's a pretty fundamental design one way or the other, and I have refactor all code in that light now, I cannot wait till Qt might change its mind in the future. So my immediate problem is to figure out what I might need to look for in my code now which may need changing as a consequence. -
Hi,
From the style sheet documentation. It’s coherent. Style sheets guarantee that what you set is what you get and only what it doesn’t handle, it forwards to the OS style.
So depending on your needs, you might want to consider implementing your own style like Qt Creator does.
-
@SGaist
Thank you as ever for your response.I have (re-)read the link, and I did not get from it that an explicit, say,
QWidget::setMaximumWidth()
would be overridden by a generic stylesheetmax-width:
. But I am not as familiar with Qt as you are.So, whether I like it or not, if an application stylesheet has
QPushButon { max-width: 100px; }
, and that suits 90% of the buttons in the app, but dotted over the code are the occasionalbtn.setMaximumWidth(XXX)
s with different numbers and not suitable for common stylesheet rules, they will have no effect. They would need to be changed tobtn.setStyleSheet("max-width: XXXpx;")
instead if I want to be sure they come into effect.Once stylesheets are in use, you must use
setStyleSheet()
and not the dedicated Qt properties to be sure to override. Right? -
Yes that's right. However, like I wrote before depending on your needs, a custom QStyle might be more suitable.