Stylesheet inheritance and "container" widgets
-
I am finding there is strange and quite undocumented behaviour on "container" widgets and stylesheets. I have noticed this as a result of my investigations into my https://forum.qt.io/topic/110974/qwidget-setstylesheet-breaks-cascade-inheritance, but it is not the same issue as that (I don't know if it might be related somehow), so I am creating a standalone thread for this.
Briefly, stylesheets on individual widget elements in a hierarchy should "inherit"/"cascade" downward to descendants. But I am finding strange behaviour with a stylesheet set on "container" widgets (i.e. those Qt Designer groups as Containers).
import sys from PySide2 import QtWidgets # from PyQt5 import QtWidgets def window(): app = QtWidgets.QApplication(sys.argv) # mw: main window mw = QtWidgets.QMainWindow() mw.setGeometry(100,100,200,250) mw.setWindowTitle("PySide2") # sw: tab widget, set as main window's central widget tw = QtWidgets.QTabWidget(mw) mw.setCentralWidget(tw) # p1: page #1 of tab widget, it is a scroll area p1 = QtWidgets.QScrollArea(tw) p1.setFixedHeight(100) tw.addTab(p1, "Page 1") # wg: widget on page, tall enough to cause scroll area scrollbars wg = QtWidgets.QWidget(p1) wg.setFixedHeight(200) p1.setWidget(wg) # main window sets color to red mw.setStyleSheet("* { background-color: red; }") # tab widget sets color to blue # affects the tab widget itself, but it has no effect on any page inside the tab widget :( # tw.setStyleSheet("* { background-color: blue; }") mw.show() sys.exit(app.exec_()) if __name__ == '__main__': window()
This creates a simple hierarchy:
QMainWindow > QTabWidget > QScrollArea > QWidget
. When run the above code produces:Good! The red is set on
QMainWindow
and inherits down toQTabWidget
and on into itsQScrollArea
and content.Now let's uncomment the line which sets a stylesheet on the
QTabWidget
:tw.setStyleSheet("* { background-color: blue; }")
Now what's going on here? The
QTabWidget
itself is blue, but theQScrollArea
remains red? It's as though theQTabWidget
"partially stands outside" the inheritance hierarchy: it inherits from its parent, but its own setting does not cascade down to its children, they inherit from higher up the tree....? It's as though we have two separate hierarchies:QMainWindow > QTabWidget > [STOP] QMainWindow > QScrollArea > QWidget
I have also tested the above with a
QStackedWidget
instead ofQTabWidget
"container", and behaviour is the same.How does this work? Where is there any mention when this applies to what widgets in the docs?
-
A follow-up post, showing even odder behaviour.
Continuing from above code. Let's make the two style lines as follows:
mw.setStyleSheet("* { background-color: red; } QScrollBar { background-color: yellow; }") tw.setStyleSheet("* { background-color: blue; } QScrollBar { background-color: green; }")
Again, this shows the scrollbar color is inherited from
QMainWindow
and ignored fromQTabWidget
, as before. Now let's comment out theQMainWindow
's stylesheet totally:# mw.setStyleSheet("* { background-color: red; } QScrollBar { background-color: yellow; }") tw.setStyleSheet("* { background-color: blue; } QScrollBar { background-color: green; }")
Hmm, now descendants are all blue & green, which comes from
QTabWidget
, which we said does not cascade to children....Sooo, what we are seeing now seems to be:
- When all we do is set style on
QTabWidget
it does cascade down to children... - ...but if something higher up the hierarchy (
QMainWindow
here, but I've tried others) happens to have a stylesheet then inheritance is taken from there andQTabWidget
is ignored.
Wot??
-
Finally, here's really scary behaviour:
mw.setStyleSheet("QPushButton { border: none; }") tw.setStyleSheet("* { background-color: blue; } QScrollBar { background-color: green; }")
I have put a stylesheet on
QMainWindow
but with a rule totally unrelated to the widgets I am showing. [I could have put anything non-empty, evenmw.setStyleSheet(" ")
.] Nothing now inherits fromQTabWidget
.Now let's change to
# or omit the following line completely mw.setStyleSheet("")
With no/empty stylesheet on
QMainWindow
, and only in this case, the background andQScrollBar
do take fromQTabWidget
. [And this finally might be related to my problem in the other thread.]Triple-wot???
-
I guess it has something to do with the fact, that
setStylesheet("...")
is aQWidget
function and not fromQTabWidget
itself.
So even setting a stylesheet background color ontw
would start atQWidget
-level (as well as setting stylesheet tomw
). The*
says ' all Widgets', so I guess thetw
stylesheet gets ignored...
Just a guess... Could be totally wrong :-) -
@Pl45m4
QTabWidget
inherits fromQWidget
, so I don't know what you mean about thesetStylesheet("...")
. Nor, I'm afraid, your other observations. The behaviour is not observed on non-container widgets. But thank you for responding.I would politely request someone look through the examples I have given (or better still try them!). Only by following through what I show will someone appreciate how odd/inconsistent the exact behaviour illustrated is....
-
Ok it IS weird
- When conflicts arise, the widget's own style sheet is always preferred to any inherited style sheet, irrespective of the specificity of the conflicting rules. Likewise, the parent widget's style sheet is preferred to the grandparent's, etc.
So normally it should behave like you expected, but it (strangely) doesn't
@JonB said in Stylesheet inheritance and "container" widgets:
QTabWidget inherits from QWidget, so I don't know what you mean about the setStylesheet("...")
Yes and because of that, I thought, setting a stylesheet to
tw
is the same as setting the same style tomw
, which isn't the case, becausemw
is parent oftw
andthe widget's own style sheet is always preferred
(At least, should be preferred)
What happens if you replace the
*
with the actual class or type selector? (Could be a missinterpretation issue of*
while cascading / inheriting) -
@Pl45m4 said in Stylesheet inheritance and "container" widgets:
What happens if you replace the * with the actual class or type selector? (Could be a missinterpretation issue of * while cascading / inheriting)
Makes no difference to the basic behaviours. Depending on which example you looking at, you could get rid of rules other than for
QScrollBar
and still see the weirdness.This also has nothing to do with at least the behaviour of #5/6, which shows in its case that the content of the
setStyleSheet()
(other than whether it is empty or not) is not relevant to the behaviour. -
I have raised https://bugreports.qt.io/browse/QTBUG-81958, as I'm not getting an answer here.