Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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:

    1. Screenshot from 2020-01-29 09-17-43.png

    Good! The red is set on QMainWindow and inherits down to QTabWidget and on into its QScrollArea and content.

    Now let's uncomment the line which sets a stylesheet on the QTabWidget:

       tw.setStyleSheet("* { background-color: blue; }")
    
    1. Screenshot from 2020-01-29 09-21-06.png

    Now what's going on here? The QTabWidget itself is blue, but the QScrollArea remains red? It's as though the QTabWidget "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 of QTabWidget "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; }")
    
    1. Screenshot from 2020-01-29 09-48-51.png

    Again, this shows the scrollbar color is inherited from QMainWindow and ignored from QTabWidget, as before. Now let's comment out the QMainWindow's stylesheet totally:

       # mw.setStyleSheet("* { background-color: red; } QScrollBar { background-color: yellow; }")
    
       tw.setStyleSheet("* { background-color: blue; }  QScrollBar { background-color: green; }")
    
    1. Screenshot from 2020-01-29 09-51-03.png

    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 and QTabWidget is ignored.

    Wot??



  • Finally, here's really scary behaviour:

       mw.setStyleSheet("QPushButton { border: none; }")
    
       tw.setStyleSheet("* { background-color: blue; }  QScrollBar { background-color: green; }")
    
    1. Screenshot from 2020-01-29 10-08-16.png

    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, even mw.setStyleSheet(" ").] Nothing now inherits from QTabWidget.

    Now let's change to

    # or omit the following line completely
    mw.setStyleSheet("")
    
    1. Screenshot from 2020-01-29 10-09-43.png

    With no/empty stylesheet on QMainWindow, and only in this case, the background and QScrollBar do take from QTabWidget. [And this finally might be related to my problem in the other thread.]

    Triple-wot???



  • @JonB

    I guess it has something to do with the fact, that setStylesheet("...") is a QWidget function and not from QTabWidget itself.
    So even setting a stylesheet background color on tw would start at QWidget-level (as well as setting stylesheet to mw). The * says ' all Widgets', so I guess the tw stylesheet gets ignored...
    Just a guess... Could be totally wrong :-)



  • @Pl45m4
    QTabWidget inherits from QWidget, so I don't know what you mean about the setStylesheet("..."). 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....



  • @JonB

    Ok it IS weird

    Cascading

    • 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 to mw, which isn't the case, because mw is parent of tw and

    the 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.


Log in to reply