Genericity, extensibility and reuse: QWidgets Vs QML
-
Hi all,
I am new to QML while I have several years of experience with QWidgets.
While you are a bit constrained by QObject limitations, you can exploit most of the C++ power to make all your widgets code reusable, easy to extend and generic.
How easy is to make the same with QML? is it even possible with the same performance of QWidgets?
Is there any kind of template keywords or things like that in QML?
-
If you ask about possibilities, then QML gives you the same flexibility as widgets. It's also based on QObjects, can be extended in C++, can be further extended in QML and JS.
There are no template keywords in QML but you can do a lot of magic with Loaders and JavaScript. Or do the magic on C++ side with QQuickItem and QQuickPaintedItem.
How easy is to make the same with QML?
To answer a "the same" question, I'll need a bit more concrete example of what you mean ;-) In general, writing QML is extremely easy, easier than QtWidgets. It's also easier to reuse QML components. And making nicely animated UIs is way, waaaay easier in QML.
Somehow, after years of experience with both I find it easier to do small tools and helper apps in widgets, but it's easier to build large applications with QML. I can't quite figure out why :D
is it even possible with the same performance of QWidgets?
Sure. Nowadays you can run both QML and Widgets on calculator-grade CPUs (MCUs), so performance is really not a problem most of the time. It used to be, yes, but not anymore. QML-heavy apps only seem to struggle with window resizing for some reason.
-
@sierdzio thank you for your reply
I am interested in this kind of magic indeed, and especially in the runtime costs. You know, with templates, you do everything at compile time, but the javascript part, at first glance, doesn't fit well here: there must be a cost for that flexibility that I would like not to pay. I am going to explore more the C++ / QML integration.
In terms of QWidgets, I exactly now when the methods (including constructors and destructors) are invoked, but still I have no clue how this works in QML. I have started to read some books about it, but there are still too many obscure things. E.g. when in QML there are things like:
mySize = compute(Y)
... when is this function invoked? how many times is being checked?
I guess I'll have to read more than one book and a lot of documentation to understand QML internals. I know there is a lot of magic with QML and for many ones is awesome as you don't have to think about the internals, but still, I think knowing how it works is very important.
It's also easier to reuse QML components.
That sounds good
Sure. Nowadays you can run both QML and Widgets on calculator-grade CPUs (MCUs), so performance is really not a problem most of the time. It used to be, yes, but not anymore. QML-heavy apps only seem to struggle with window resizing for some reason.
Regarding the performance, I still remember a discussion where everybody was suggesting to switch to QML for mobile as QWidgets does not support GPU or something like that. It was a discussion of some years ago, but perhaps it is still relevant
Cheers
-
but still I have no clue how this works in QML
[...]
I guess I'll have to read more than one book and a lot of documentation to understand QML internalsI highly recommend trying to write a few simple projects to get to know it better. I remember it took me some time for the whole concept to "click" in my head, and then it was much easier to write QML but also to understand various descriptions and docs. It's not that complicated once you get the idea behind it all.
E.g. when in QML there are things like:
mySize = compute(Y)
... when is this function invoked? how many times is being checked?It depends on other circumstances. Here's a short summary:
QML is a declarative language. It means you declare how you want it to work, and QML engine will do it for you. So for example, when you write this:
Rectangle { width: 50 height: width }
You instruct the engine that
height
should always be the same aswidth
. So every time you modify that rectangle'swidth
, theheight
will change, too. This is called abinding
in QML. Bindings are evaluated:- when component is created (~when you first show it)
- when any value within the binding changes
That's it - if a binding does not need to be updated, it won't.
Binding can be very simple (like above), and QML engine is very clever about keeping simple bindings very well optimized. They can also be more complex (big JavaScript functions), then JIT takes over (they are compiled to machine code o first run, then they execute very fast).
Now, any JS code (in QML slot, or in a binding) can do something else: an assignment. Like you wrote in your example:
mySize = compute(Y)
An assignment is done once. It's traditional, imperative code, like in C++.
mySize
will be set to the outcome ofcompute(Y)
and that's it. If something modifies the value ofY
, the assignment will not be reevaluated (unless when it's a part of a complex binding but let's not worry about this too much now).
So such line executes only once, for example when invoked like this:Component.onCompleted: { mySize = compute(Y) }
If you move that line to a slot which is invoked a lot, then it will be executed a lot. But here the choice is entirely up to you, you decide what and when to execute. In bindings, it was all automatic.
To sum it up:
- QML is a declarative language, parsed at launch (well, at first launch only because nowadays results are cached + there is QML compiler)
- bindings are updating values automatically when their parts change value
- but QML also can include imperative code (in slots, complex bindings), here you have more control when it is executed
- an assignment can overwrite (break) a binding
It's probably still not clear to you now :-) Feel free to ask more.
-
@sierdzio thank you again for your reply: a really clear explanation.
I am definitely checking out some examples and reading books and documentation in parallel. THank you for the advice.
Just a few questions:
-
height: compute(width) ->is this considered to be an assignment? if so, will this be evaluated just once or every single time width changes?
-
let's suppose we have N components and each of them has some values that are bound (in the QML meaning). Let's also suppose that a QTimer changes most of these values every X milliseconds.
How the refresh/repaint is implemented in this context? if every time a bound value changes, the GUI is update it would not be ideal. I wonder if there is a way to force the update once every Y milliseconds or to tune these kind of settings.
Thank you again!
-
-
@genner said in Genericity, extensibility and reuse: QWidgets Vs QML:
@sierdzio thank you again for your reply: a really clear explanation.
I am definitely checking out some examples and reading books and documentation in parallel. THank you for the advice.
Just a few questions:
- height: compute(width) ->is this considered to be an assignment? if so, will this be evaluated just once or every single time width changes?
This is still a binding and will be recalculated every time
width
changes. What comes after:
is a JavaScript code block, you can put any kind of computation there. Typically it's good to keep them simple, and as said QML engine is very good at optimizing simple bindings.- let's suppose we have N components and each of them has some values that are bound (in the QML meaning). Let's also suppose that a QTimer changes most of these values every X milliseconds.
How the refresh/repaint is implemented in this context? if every time a bound value changes, the GUI is update it would not be ideal. I wonder if there is a way to force the update once every Y milliseconds or to tune these kind of settings.
If a visual property changes, it will trigger a repaint. Not all QML components are visible, not all properties contribute to how an item looks. Also, if an item is disabled, it won't cause visual update. But of course all bound properties are still getting updated.
You can control this if you implement your component in C++, then you can choose when to mark item as dirty and in need of redrawing. See https://doc.qt.io/qt-5/qquickitem.html#update
Thank you again!
-
Ok now it's much clearer, thank you again.
It looks like there is a lot of documentation to read, but apparently most things I have in mind should work. Still, for some reason, I feel QWidgets will perform faster. I'll try to make some tests and get back here.
Thank you again!