Say No to Qt Style Sheets Making the Right Choice Between QStyle and Qt Style Sheets, Upfront
You have two choices when it comes to giving a custom style to your Qt widgets.
Qt Style Sheets are very convenient for getting started — just a few CSS-like rules, and they work.
It is our experience, however, that Qt Style Sheets create too much trouble and a QStyle subclass (*) gives a better solution, in the long run.
The following chart compares the two choices, to show you why a QStyle subclass trumps Qt Style Sheets:
Scenario |
Qt Style Sheets |
QStyle |
Calling setFont/setPalette |
No effect (rather unexpected), even if the style sheet has nothing to do with colors |
Works as usual |
Changing the system colors |
No effect — the styled widgets don’t adapt to the new colors, even if the stylesheet has nothing to do with colors |
Works as usual — the widgets use the new colors via the updated QPalette |
Achieving 100% of the styling requirements |
Style sheets allow you to do a number of things, but not everything. If there’s no support for a specific customization, there’s no solution. |
QStyle subclasses in C++ give full control, as long as the widget actually delegates to the style. (Note that style sheets are implemented as an internal QStyle subclass. So, by definition, the flexibility given by custom QStyle subclasses is greater or equal.) |
Mixing custom styling with native styling |
Stylesheets do not mix well with QProxyStyles or other QStyle subclasses. This makes it even harder to gradually move away from the stylesheet solution. One specific example for proof: a stylesheet that simply sets a background color will lead to many of the style’s methods not being called because the style sheet style does many things on its own (ex: CT_SpinBox). |
QProxyStyle allows you to tweak just one aspect and leave the rest to the native style for the platform. |
Making a specific window unstyled (e.g., file dialog) |
Very complicated (impacts all CSS rules) |
Simply call QWidget::setStyle on other windows and not on that one. |
Scaling for complex needs |
Actual management of the stylesheet data does not scale (no include mechanism, etc.), calling for super-complicated selectors. |
The C++ code of the QStyle subclass can call into helper classes, one per type of widget. This is the way KDAB develops widget styles these days, to avoid writing a 20000-line class. |
Reacting on changes |
Selectors on properties do not get reevaluated when the properties change, forcing hacks such as reset of a style sheet with the associated cost. |
The setter calls update(); the paint event calls into the style again. |
Performance considerations when styling many independent widgets |
Each call to setStyleSheet triggers parsing, which can be slow if done too often (e.g., when creating 50 buttons, each one calling setStylesheet(“…”)). The usual solution is a single application-wide stylesheet, but that’s a pain to maintain and it hits the issue of management of the style sheet data, above. |
No parsing necessary. Either all widgets use the application style, or individual widgets can point to a specific style. |
Performance considerations when reparenting a widget |
Each reparenting triggers clearing the stylesheet rules cache and recalculating everything. |
Reparenting has no performance impact with QStyle. |
(*) To be clear, when we say “a QStyle subclass,” we don’t mean you should subclass QStyle directly. Usually, you would start by subclassing QProxyStyle, so that all widgets still appear on the screen. Then you can style them incrementally and, when you’re done, you can switch to QCommonStyle as the base class, if the appearance should be the same on all platforms.
As you can see, we find that a QStyle subclass is overall a much better solution for styling widgets than is Qt Style Sheets. It’s a bit more difficult to write, but the mechanism is much more powerful and performant.
If you would like the KDAB experts to help you write such a widget style, don’t hesitate to contact us.
If you like this article and want to read similar material, consider subscribing via our RSS feed.
Subscribe to KDAB TV for similar informative short video content.
KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.
Are there online example of modern theming using QStyle subclass?
Also I would like to comment that stylesheets can use the current palette in their definition
ex:
QAbstractButton {
border-color: palette(highlight);
color: palette(bright-text);
}
https://doc.qt.io/qt-6/qstyle.html has some example code, as well as https://doc.qt.io/qt-6/qtwidgets-widgets-styles-example.html. The look is definitely not modern, but the API hasn’t changed, and everything you can do with a stylesheet, you can do with a style subclass (and more).
Indeed stylesheets can call into the palette. The default behavior though, when the stylesheet doesn’t mention colors, doesn’t play well with the palette. See the documentation for QWidget::setPalette which says “Warning: Do not use this function in conjunction with Qt Style Sheets.”. And I remember hitting such problems, they are not just theoretical. I don’t have a testcase at hand though.
I really like the blog posts from KDAB and already learned a lot of interesting things from it, but this time I diasgree.
Styling Qt application with styleheets is a lot easier then using QStyle. Using styleheets allows for a very good separation between UI implementation and design. If you know CSS from HTML, then you already know how to use Qt stylesheets.
We styled a complete application with CSS (https://cetoni.com/cetoni-elements/) and we do not see any performance issues. Some of the mentioned points in the blog post are quite “theoretical” and do not matter in most real world applications. CSS gives you a great flexibility to quickly fix or change the styling without the need to change and recompile C++ source code. Changing and testing style changes with CSS is extremely quick.
The use of the Qt Advanced Stylesheets Library (https://github.com/githubuser0xFFFF/Qt-Advanced-Stylesheets) makes Qt stylesheets even more powerful and allows you to switch the complete application theme including Palette and Icon colors using Qt stylehsheets (https://youtu.be/xWTpCwCz8dI).
Hence my opinion: Say Yes to Qt Style Sheets ;o)
We have a widgets app styled completely with style sheets and we’re happy with it. I did not find the QStyle method to be very accessible.
We’ve integrated the SASS stylesheet compiler into our app so that we can determine some parts of the style dynamically, based on platform, DPI, and potentially user preferences like high contrast etc. This gives us access to much better syntax and better selectors too.
I’m glad to see diverging opinions, debating makes things interesting 🙂
The need to write an additional library on top of what is provided by Qt, partially proves the point that the stylesheet support in Qt is not completely optimal, though.
About the points being theoretical: they all come from our own actual experience. For instance the one about wanting not to affect the style of file dialogs on Linux is the reason I ported the opensource music application rosegarden from Qt stylesheets to a QStyle.
I work for a large game developer which uses Qt extensively in its tooling. We use QSS across our tools and have found that QSS does in fact extract a pretty substantial performance cost in instantiating widgets, but also reparenting widgets (as the style is reset).
What this means is that ordinary small dialogs and general UI will not pay a measurable price, really depending on the number of widgets you have in any given panel. However if you are displaying any UI with many widgets or have panels which regularly need to rebuild complex layouts (such as a property editor panel) you will pay a very high cost. On modern machines the “polish” style update phase for widgets will be several milliseconds each, which adds up pretty quickly for situations where there are many widgets.
So the general rule of thumb is: few widgets with static layouts and few refreshes – you’ll probably be fine – many widgets in complex nested layouts, with frequent tear-downs and refreshes – you will run into some substantial trouble.
Today I hit more limitations in Qt stylesheets, proving the point that they don’t allow implementing 100% of a style requirement that doesn’t keep within those limitations. Qt stylesheets allow to set the `text-align` property to right-align the text for a QProgressBar, but there’s no way to add some padding to the right of that text so it doesn’t collide with a rounded rect for instance (because the text isn’t a subcontrol, and there’s no property like text-padding). Similarly, a stackoverflow post (https://stackoverflow.com/questions/68441094/qt-stylesheet-controlling-progressbar-text-highlight-color/73387635#73387635) points out the inability to change the color of that text when the progressbar runs into it. And finally, there’s an issue with rounded rectangles of very small sizes (e.g. for a 1% progress value), the stylesheet code gives up making it rounded and just makes it square, while `QPainter::drawRoundedRect()` actually works when doing this in a QStyle. I’ll soon record a youtube video for the KDABTV channel, to show this more graphically.
I certainly need look closer at QStyle. Currently, I’m just using Qt Style Sheets, as it’s served my needs. The first question that comes to mind is can the user supply style information on their own to the app and have it drive QStyle? Today, one of the strong benefits of using Qt Style Sheets is quite often an end-user will ask “how can I change such and such” and want to change a specific font or color on some random widget you never even thought about needing a user preference. And it’s nice to just point them to writing a simple style sheet and passing it as a command-line argument when calling the app and presto, it works like magic as if we were thinking of their need to change that widget from the beginning. Will that still work with QStyle (or can be made to work)?
In the documentation of QStyle, I see it says “Warning: Qt style sheets are currently not supported for custom QStyle subclasses. We plan to address this in some future release.”. Does that relate to being able to pass external style sheets to the app for driving that as I describe above? Also, is that specific to just Qt 6 or does it go back to Qt 5 or even Qt 4 days too?
There’s some good sounding benefits of QStyle, as described, but I need to learn more about it.
The ability for users to customize things is undoubtedly the appeal of stylesheets, and QStyle doesn’t offer that out of the box (the only way to get anywhere near that is to read configuration from the C++ style code, which allows for some things but is of course not as flexible).
“Qt style sheets are currently not supported for custom QStyle subclasses” doesn’t really make sense to me, actually.
I don’t see how this is worse than the usual use of stylesheets, applied on top of the default widget style. I suppose someone hit some of the problems mixing styles and stylesheets (as in our table row that talks about mixing) and added this as documentation (I can’t get more details though, this bit of documentation predates the public git repository for Qt).
So yeah, I think it makes sense to provide a QStyle and still let users play with stylesheets on top for light customization, as a best-of-both-worlds compromise.
All this applies to Qt 4, 5, and 6, there hasn’t been a major architectural change in that area.
One issue I’ve experienced with QStyle & Qt stylesheet compatibility is to do with that the underlying QStyleSheetStyle class doesn’t provide public APIs for many of the basic details specified in .qss. E.g. you can’t easily get access to the box model parameters applied to any given widget, so any QStyle code other than QStyleSheetStyle can’t easily conform to the design specified in the stylesheet in many cases. This is compounded by what seems to be a reluctance of Qt themselves to do anything about it…..
For example there’s a long standing bug that Qt seem to refuse to fix as it seems to just get closed everytime it’s reported, namely where if the stylesheet tries to apply “dynamic” styling to tab bars/tab widgets (e.g. using “selected” psuedo state etc.) the underlying cache implementation for stylesheet evaluation doesn’t update the space calculation for the tab text so you can easily get the text colliding with the tab outline, and since the details are private, there’s no way to fix it, other than maintaining your own patch to Qt. Sorry I don’t have details to hand to list the QT-BUGs invloved….. and it’stricky to find as it goes back to trolltech days…….
BTW, Qt stylesheets have one big benefit – they are very easy writable and readable, and sometimes it matters.
P.S. Maybe, if there were such a style that can get paintings from the SVG file items, than it were much better -> SVG controls are MUCH more easier to draw vs to do it with raw programming.
Hello,
Thanks for your comment. There’s no doubt that stylesheets are easier in many ways, including writing and reading. Which is why, if they do the job for the task at hand, they’re fine. But I wanted to raise awareness that their possibilities are limited, and therefore writing stylesheets for complex styling needs, is a risky investment: if you can’t negotiate with the style designer to remove requirements that can’t be implemented with stylesheets, you’ll end up having to scratch it all and rewrite it as a widget style…. See the example I mentioned in an earlier comment about the progressbar text colliding with the right edge of the groove; no way to move it a bit to the left. Then what…. That stylesheet was indeed easy to write, but the issue is not fixable…
In terms of SVG, that’s certainly feasible. The common solution is to use QSvgRenderer in the widget style, to load the required SVG files from disk (or qrc resource) and paint them each into a QPixmap that’s kept around. QPixmaps are then fast to blit on screen; rendering SVG – or worse, parsing SVG – at every paint operation is quite costly so we don’t want that.
Completely understandable, but then don’t title the article “Say No to Qt Style Sheets”. A better title would’ve been “The Pitfalls of Qt Style Sheets” or something. It would be less decisive and adversarial.
Wow, I rarely comment, but thanks so much for this post.
I was so confused because:
1. I had a `QFrame` containing a `QLabel`.
2. I set the style sheet of the `QFrame` to stylize its handle (no foreground/background color elsewhere).
3. Then I copied the `QFrame` palette, overrode the color of its `QPalette::WindowText` role, and set the palette of that same `QFrame` to that changed one.
Only to discover that, even if the palette of the contained `QLabel` gets updated, its foreground color remains unchanged!
That’s a terrible design, Qt!
I mean especially for a modular design. If my custom widget uses specific roles of its palette, it doesn’t need to know whether or not there’s a style sheet above.
A 100% style sheet approach with easy access to computed property values would work. Having widget-specific style sheets is weird and confusing. In the HTML/CSS world, they all get combined into a list of rules. Then a global style would correspond to a base style sheet, and you could override parts of it with classes, cascading, inheritance, initial values, etc.
In the meantime, back to `QStyle`!