Does Qt need a modern C++ GUI API?
-
[quote author="c++freeloader" date="1335391023"]C++ has been used on an uncountable number of "real" applications.[/quote]
Altough usually not as a pure declarative object-defining data language. Either XML or some custom solution has usually filled that role, even for C++ applications.
[quote author="c++freeloader" date="1335391023"]Well, if you don't like that method, you can pick from 20 other C++ ways to do the same thing. If I don't like "named property statements" in QML, what other choices do I have? Not many.[/quote]
20 different-looking ways to do the same simple thing, is not exactly a readability and maintainability dream come true. At least not unless all code you will ever deal with was written by yourself (and you can still remember what you thought when you wrote it).
[quote author="c++freeloader" date="1335391023"]Just like any other signal somehow "knows" when it's been emitted. Something in the implementation does the emitting.[/quote]
But "mouser.containsMouse" is not a signal, it's a property (which at any point in time can be either true or false).
[quote author="c++freeloader" date="1335391023"]Something must translate from the declarative description into something that is processable by the CPU/GPU.[/quote]
No, into an internal declarative description that is processable by the Qt Quick engine.
Unless you choose to embed JavaScript expressions or GLSL shader snippets inside your QML source, I don't think there will be any byte code compilation of any sort. The only thing that gets passed to the CPU is the compiled Qt Quick engine's code (which was written using C++).
-
[quote author="jdavet" date="1335455361"]20 different-looking ways to do the same simple thing, is not exactly a readability and maintainability dream come true. At least not unless all code you will ever deal with was written by yourself (and you can still remember what you thought when you wrote it).[/quote]
The point is that in the real world, there are many ways of doing things for a reason. A language dictating the "one-and-only correct and true way" is a language that hasn't yet been used in the real world. A book for a 3 yr old is easy to read too. Doesn't mean that it's going to be considered a literary class.[quote author="jdavet" date="1335455361"]But "mouser.containsMouse" is not a signal, it's a property (which at any point in time can be either true or false).[/quote]
A property that has the semantics of a signal.[quote author="jdavet" date="1335455361"][quote author="c++freeloader" date="1335391023"]Something must translate from the declarative description into something that is processable by the CPU/GPU.[/quote]
No, into an internal declarative description that is processable by the Qt Quick engine.[/quote]
Which is ultimately imperatively executed by a CPU/GPU. Do you like arguing for arguing sake or do you actually have a point? -
QML introduces a significant performance overhead, due to beeing executed in a JavaScript-based virtual machine, and is introducing lots of glue code, glue abstract and proxy objects.
QML is analyzed once at the time a source file is loaded to build a native representation, which then provides the same performance as beeing created in native C++ code. A compiler is beeing evaluated, which would shift analysis from run-time to compile-time, but as performance metrics have shown that the current run-time overhead is minuscule, it is currently on low priority.
QML (as beeing a declarative language) doesn't require JavaScript, it is (theoretically) possible to build and use Qt Quick and QML without any JavaScript interpreter at all. Qt Quick uses JavaScript just to evaluate imperative code and non-trivial bindings. Everything else results in native code.
There is no requirement for additional glue code or proxy objects to expose data from C++ to QML. Existing objects (QObject), collections (QStringList, QObjectList) and models (QAbstractItemModel) can be directly exposed to and used in QML, signals and slots are routed transparently and both, C++ and QML, can modify object properties. For doing so, Qt Quick utilizes the Meta-Object Information that is generated by <code>moc</code> and already present for every object in Qt.
Former native applications become non-native due to the use of QML.
QML is used to build a native representation, as other non-native, domain-specific, interpreted languages already present in Qt and every Qt application, like CSS, SQL or PCRE.
QtQuick requires applications to be created in QML and JavaScript, the advantages of the new graphical arichtecture (SceneGraph) can be used from QML only and C++ has been deliberately left out to push QML, which has made QtWidgets and The Graphics View Framework obsolte.
QtQuick exposes the API to C++ where meaningful and the SceneGraph can be used using both, QML and C++, one without the other. This includes all the building blocks for QtQuick, including the Animation Framework, the State Machine Framework and the Graphics View Framework (all of them beeing there before QtQuick). Every object created in QML is accessible in C++ and QML is fully extensible using C++. However, there is a trade-off between exposing internal classes of QtQuick and thus introducing binary contracts and the value to the developer (which is actually low, as there is no support for most QML features in C++).
The application and the business logic itself is still expected and required to be made in C++, because QML is mainly used to build user interfaces, not applications - the same way CSS is used to style QtWidgets, SQL is used to query data and PCRE is used to do string analysis.
QtWidgets and The Graphics View Framework are still first class citizens in Qt, they still can be used and will be used to create applications, they will receive attention on part of the developers and there are still additions made (there has been a quite impressive charting component released just a few weeks ago). QtQuick is optional, there is no obligation for new applications to use it, and there is no requirement to port existing applications.
However, you cannot use SceneGraph with QtWidgets, because the imperative drawing model of QtWidgets is quite incompatible with the state-based drawing model of a modern GPU (where almost all the additional performance of QtQuick comes from). This is a technical limitation.
There is no way to debug declarative user interfaces created using QML, above all not using GDB.
GDB is a debugger for imperative languages, Qt Quick / QML has its own debugger suitable for declarative languages.
There are no advantages of using QML over C++.
QML is fully network transparent, each resource can be either local or remote, including source files. In addition, there is full tool support for creating QtQuick user interfaces, which also allows for doing user interface-specific code by the designer, who usually isn't aware of C++, and thus enabling another layer of user interfaces / business logic abstraction. It is absolutely easy to learn and has a much less steep learning curve for people not beeing aware of imperative programming languages in general and C++ in specific. Beeing a declarative language featuring an imperative interpreter one gains the advantages of lazy evaluation and (non-trivial) bindings. In addition, there are almost no binary contracts introduced, which is vital for emerging technolgies.
This is not possible in C++.
-
QML is a proprietary language, it was made closed-door and political motivated, as any decisions regarding Qt. Declarative langues are quite uncommon for creating user interfaces.
QML with its JSON-like syntax and imperative JavaScript language is the result of a technical selection process. The specification and implementation is publicy available, and can be searched, modified and extended by everyone. Decisions on the future of Qt were always takes on purely technical reasons inside Nokia before open governance was fully set up. Now decisions are taken in the public — and again for technical reasons only.
Declarative user interfaces-languages are widely used and pushed, for example XAML, HTML5, XUL, SVG and Cocoa.
QtQuick / QML is a toy-language only, no real spplications have been ever created with it.
Although QtQuick / QML beeing an emerging technology, both are activly used to create and port applications, like KDE, Qt Media Hub or the Nokia N9.
C++ can be used as a declarative language as well.
QML is declarative, not imperative - JavaScript is, as well as C++. You cannot express one using the other. A C++ QtQuick API will always be imperative.
Qt5 is a primarily a QtQuick release. There are no other improvments for the classical development.
- All ports now base on the Qt Platform Abstraction Layer
- Modularization of the Qt Respository
- Improvments for QtCore, including a new way of handling standard pathes, a full-blown JSON parser, MimeType recognition, a completely new compile-time checked signal/slot connection syntax, which allows for using of C++11 lambdas, a completely new Perl-compatible regular expression parser, a rewrite of many data structures optimized for better performance and C++11 support
- Improvments for QtGui, including a port to the new QPA architecture, top-level surfaces and built-in OpenGL support
- Improvments for QtNetwork, including support for DNS lookups
- Integration of Qt3D, QtLocation, the Mobility APIs, improvments for QLocale, time handling, unicode, an updated WebKit, ...
Dynamic fluid interfaces are just for smartphones, which are still a niche market. Desktops require native looking and behaving applications.
In 2011 415 million desktops were sold. In the same time 488 million smartphones were sold, about 700.000 smartphone applications were created, 17 billion applications were either sold or downloaded for free and this number is expected to rise to 185 billion in 2014. A revenue of over 15 billion dollars has been generated, which is an increase of 190% over 2010. There is a strong momentum in classical desktop application development for user-centric and task-centric applications as well, which breaks with native platform-specific look and feel. The predominant desktop platform Windows (92.5%) completely focuses on task-centric applications within its upcoming release (Windows 8).
X is better than Y, and should take precedence over Z.
No, but there are facts, and there is fiction. Basis of discussion should be the former, not the latter.
-
[quote author="Lukas Geyer" date="1335519690"]QML introduces a significant performance overhead, due to beeing executed in a JavaScript-based virtual machine, and is introducing lots of glue code, glue abstract and proxy objects..[/quote]
The real answer is that we simply don't know how well QML will scale in non-toy apps, because no non-toy apps have been developed. Or at least no one who has developed such a non-toy app has been willing to go public. We also know that a native implementation would be monotonically more performant than the QML version. Just what the delta is, we don't know[quote author="Lukas Geyer" date="1335519690"] QML (as beeing a declarative language) doesn't require JavaScript, it is (theoretically) possible to build and use Qt Quick and QML without any JavaScript interpreter at all. Qt Quick uses JavaScript just to evaluate imperative code and non-trivial bindings. Everything else results in native code.[/quote] Except that "everybody" says that the non-trivial bindings are the real reason to use QML. So in theory, you then don't need QML, because if bindings are not needed, there's no good reason to use it.
[quote author="Lukas Geyer" date="1335519690"] There is no requirement for additional glue code or proxy objects to expose data from C++ to QML. Existing objects (QObject), collections (QStringList, QObjectList) and models (QAbstractItemModel) can be directly exposed to and used in QML, signals and slots are routed transparently and both, C++ and QML, can modify object properties. [/quote] Unless my objects aren't one of QML's predefined things it understands and unless my signals and slots are implemented using Boost signal or some such thing.
[quote author="Lukas Geyer" date="1335519690"] QML is used to build a native representation, as other non-native, domain-specific, interpreted languages already present in Qt and every Qt application, like CSS, SQL or PCRE.[/quote] Except that these other things don't completely dominate the application, are not a requirement to use the technology, and there are C++ interfaces for all these things.
[quote author="Lukas Geyer" date="1335519690"]QtWidgets and The Graphics View Framework are still first class citizens in Qt, they still can be used and will be used to create applications, they will receive attention on part of the developers and there are still additions made (there has been a quite impressive charting component released just a few weeks ago). QtQuick is optional, there is no obligation for new applications to use it, and there is no requirement to port existing applications.[/quote] There you go, not listening again. Many people have explained their concerns regarding this matter and it has nothing to do with "I can't use QWidgets or QGraphicsView".
[quote author="Lukas Geyer" date="1335519690"]QML is fully network transparent, each resource can be either local or remote, including source files.[/quote] You seem to be fond of this, but not even any other fanboys seem to care. And it's not like a C++ declarative (yes declarative) API couldn't easily be made to do the same thing.
[quote author="Lukas Geyer" date="1335519690"]In addition, there is full tool support for creating QtQuick user interfaces, [/quote] Yes, this is where the trolls have been burning their time instead of doing stuff that truly provides value. That's part of the list of concerns.
[quote author="Lukas Geyer" date="1335519690"]which also allows for doing user interface-specific code by the designer, who usually isn't aware of C++, and thus enabling another layer of user interfaces / business logic abstraction. It is absolutely easy to learn and has a much less steep learning curve for people not beeing aware of imperative programming languages in general and C++ in specific.[/quote] I really wish there was one "designer" who would stand up and validate your hypothesized use-case. A designer who isn't familiar with C++ and who came up to speed on QML and is using it in the way you imply. Let me know when one of those guys shows up.
[quote author="Lukas Geyer" date="1335519690"]Beeing a declarative language featuring an imperative interpreter one gains the advantages of lazy evaluation and (non-trivial) bindings. In addition, there are almost no binary contracts introduced, which is vital for emerging technolgies.
This is not possible in C++.[/quote]
How about you back up your statements with real examples which illustrate these supposed advantages and let someone familiar with C++ poke holes in your fallacious beliefs. -
[quote author="Lukas Geyer" date="1335519708"]QML is a proprietary language, it was made closed-door and political motivated, as any decisions regarding Qt. Declarative langues are quite uncommon for creating user interfaces.
QML with its JSON-like syntax and imperative JavaScript language is the result of a technical selection process. The specification and implementation is publicy available, and can be searched, modified and extended by everyone. Decisions on the future of Qt were always takes on purely technical reasons inside Nokia before open governance was fully set up. Now decisions are taken in the public — and again for technical reasons only.[/quote] If you can point to anybody else who uses QML, then I might put some stock into what you're saying.
[quote author="Lukas Geyer" date="1335519708"]Although QtQuick / QML beeing an emerging technology, both are activly used to create and port applications, like KDE, Qt Media Hub or the Nokia N9.[/quote] You probably shouldn't use a dead-end phone with a dead-end OS as the shining example of where QML can be used. I don't know the details of KDE, so it's hard to know if their adoption of QML is due to technical merit or because there is no other choice, seeing how they're going to have to use it in order to access future Qt technology.
[quote author="Lukas Geyer" date="1335519708"]C++ can be used as a declarative language as well.
QML is declarative, not imperative - JavaScript is, as well as C++. You cannot express one using the other.[/quote] Another of the complete fallacies you try to hoist on unsuspecting people.
[quote author="Lukas Geyer" date="1335519708"]Qt5 is a primarily a QtQuick release. There are no other improvments for the classical development.
- All ports now base on the Qt Platform Abstraction Layer
- Modularization of the Qt Respository
- Improvments for QtCore, including a new way of handling standard pathes, a full-blown JSON parser, MimeType recognition, a completely new compile-time checked signal/slot connection syntax, which allows for using of C++11 lambdas, a completely new Perl-compatible regular expression parser, a rewrite of many data structures optimized for better performance and C++11 support
- Improvments for QtGui, including a port to the new QPA architecture, top-level surfaces and built-in OpenGL support
- Improvments for QtNetwork, including support for DNS lookups
- Integration of Qt3D, QtLocation, the Mobility APIs, improvments for QLocale, time handling, unicode, an updated WebKit, ...
[/quote] No one is complaining about these other improvements. But just imagine what they could have done if they had spent the QML infrastructure development man-years instead on real value added stuff like the stuff you mention.
[quote author="Lukas Geyer" date="1335519708"]Dynamic fluid interfaces are just for smartphones, which are still a niche market. Desktops require native looking and behaving applications.[/quote] Again, you have it backwards. If people didn't care about the new "fluid interface" technology in QT and were perfectly happy with existing QWidgets, there wouldn't be the uproar. And you're implying that the myriad reasons people have stated regarding the need for C++ API don't apply to smart-phones. When in fact they apply just as much.
[quote author="Lukas Geyer" date="1335519708"]In 2011 415 million desktops were sold. In the same time 488 million smartphones were sold, about 700.000 smartphone applications were created, 17 billion applications were either sold or downloaded for free and this number is expected to rise to 185 billion in 2014. A revenue of over 15 billion dollars has been generated, which is an increase of 190% over 2010. There is a strong momentum in classical desktop application development for user-centric and task-centric applications as well, which breaks with native platform-specific look and feel. The predominant desktop platform Windows (92.5%) completely focuses on task-centric applications within its upcoming release (Windows 8).[/quote] Do you actually have a point in this gibberish? BTW, quoting number of cell phones sold and how big of a market it is, doesn't really bolster the case for QML, because 1) how many of these phones could actually run a QML app 2) how many of those are not based on a dead-end OS. 3) what phones does Nokia expect to field in the upcoming years that are going to be able to run QML 4) what percentage of smart-phone developers in their right mind would even think about using QML today - the answer, roughly 0 with a possibly rounding error.
[quote author="Lukas Geyer" date="1335519708"]No, but there are facts, and there is fiction. Basis of discussion should be the former, not the latter.[/quote] So start showing "facts" then. You seem to hand-wave all the supposed advantages of QML (which for all the handwaving are a mere "fiction" so far) while poo-pooing legitimate concerns of C++ users. Put your money where your mouth is and show us some real QML examples that just "cannot be done using C++". Because that's what it all comes down to. The only reason to boil the oceans for QML is if it has significant advantages All the other verbiage of your last two posts are really irrelevant at the end of the day. Let's see these advantages laid down in concrete examples.
-
I like the vision of your most recent post. If you have the time, would you mind expanding on your thoughts about the proper way to create a C++API that takes advantage of the language and provides alternate ways to achieve the end-effect of bindings?
-
bq. If you can point to anybody else who uses QML, then I might put some stock into what you’re saying.
French Telecommunications company Free uses QML on their quite successful Freebox:
http://qt-project.org/videos/watch/qt-and-qt-quick-on-the-freebox-player-set-top-box
RIM exposes a QML API together with its Cascades UI engine:
http://nerd.web.id/index.php/2012/03/22/playbook-tat-cascades-on-mobiletechcon-munich/
-
[quote author="capisce" date="1335553426"]bq. If you can point to anybody else who uses QML, then I might put some stock into what you’re saying.
French Telecommunications company Free uses QML on their quite successful Freebox:
http://qt-project.org/videos/watch/qt-and-qt-quick-on-the-freebox-player-set-top-box
RIM exposes a QML API together with its Cascades UI engine:
http://nerd.web.id/index.php/2012/03/22/playbook-tat-cascades-on-mobiletechcon-munich/[/quote]
I'll have to take a look at the Cascades UI engine. Could be interesting.
With regards to the Panasonic one, I thought it was clear that I was talking about using QML as their own framework interface language, but I guess I wasn't explicit. He was talking about just how open and available QML is for the rest of the world to use. And what I should have said is "show me who else is using QML as their language for specifying UI's". Not just using QML to interface to the Qt engine.
-
[quote author="capisce" date="1335553426"]
RIM exposes a QML API together with its Cascades UI engine:
http://nerd.web.id/index.php/2012/03/22/playbook-tat-cascades-on-mobiletechcon-munich/[/quote]
This actually looks nice. Ironically, those fellas must not be that keen on QML, since, if my eyes do not deceive me, they have chosen XML syntax instead. And furthermore, this only makes the need of a C++ API with similar capabilities even more desirable, as again, QML does not enable this kind of UI, it just describes it, the reason there isn't a similar out of the box UI API for C++ is... well, no one bothered to make it.
Also - all those showcases you are pimping are the product of scores of developers and companies with plenty of cash, who do have the manpower and resources to extend QML, it is a completely different story than single developers or smaller groups, where the avoidance of compartmentalization is a very good thing.
I still haven't seen one solid argument of WHY should QML be a necessity, the closest thing was the runtime binding, an easy analogue of which can be achieved as simple as overloading a virtual method of the root object.
@c++freeloader - my impression that sharing any type of thoughts here will never go beyond chit-chat has been reaffirmed so many times, I really don't want to bother. I've already started looking in other directions and setting the blueprint for a cross platform API, I've already got the core backbone, display, input and event loop running on win, mac, linux, ios and android, now I am torturing myself with graphics, progress is slow but at least it has a chance to manifest (better late than never). I actually plan to make the process of building the framework as C++ programming video tutorials, knowing how the framework will make it easier for people to use, that's why I try to keep the code simple, unlike the mess that Qt (and many other) private backend APIs are. When I get its basic APIs running I will expose the framework, hopefully there will be interest and more people will join in the development, so we can have a complete and truly open solution with no corporate interest behind the wheel. It will take time thou, since I am alone and develop only in my spare time, having a full time job and all..
-
@c++freeloader
How about you write a C++ equivalent of some real-life application QML code, and we can compare the merits of that code to the QML code? I would suggest this QML code, as it's quite idiomatic and exemplary, and yet not absurdly trivial: https://qt.gitorious.org/qt-labs/meespot/blobs/master/qml/AdvancedTextField.qml
-
[quote author="capisce" date="1335560638"]@temp
Have you looked at making your API on top of QQuickItem and QSGNode which are public API and give you access to the raw power of the scene graph from C++, which earlier seemed to be the main thing you desired?[/quote]
Desired not as a final goal but a direction of development focus... but yes, however it seems a little backward, if you know what I mean. I feel like I will have an easier time tapping into OpenGL benefits from scratch than conforming Qt's APIs to my vision. I've already got some sweet results with SDL and Cairo, now trying to integrate an OpenGL accelerated painter. Shader drawing is just too fast to skip it, and it is interesting too, I never really worked with low level OpenGL until now.
From my Flash days I really find the design and workflow paradigms behind Flash truly awesome, the only thing wrong with Flash is ActionScript, although it is still relatively close to C style performance is just awful. And all those "let's make it easier than C++" ideas that don't really deliver, like instead of "string name" you have to do it "var name:string" - and numerous other small nonsenses. So my idea is to do a "Flash done right" with declarative stage items, some additions and native C++, running in browser is simply not worth 10 times performance penalty, especially if the framework supports all major platforms natively.
-
[quote author="temp" date="1335559794"]
I still haven't seen one solid argument of WHY should QML be a necessity[/quote]The only argument: Nokia has made a modern mobil UI.
But!
The majority of QT users use the Qt only for multiplatform C++ desktop framework. -
@capsice
I went through the exercise of figuring out how to do the one-to-one mapping of QML to C++ for the example you asked me to look at. I repeat the example code first and then follow it with the C++ version. Before I do that, however, I wanted to make a few other comments...
As I went through this exercise a lot of questions came up as to when exactly the bound properties are read and how they exactly are updated under the hood. In looking at qquickitem.cpp and qquickrectangle_p.h and qquickrectangle_p_p.h, I now understand some of the other points that temp/Dian was trying to make. One point is that it is very difficult for an outsider to make any sense out of the QtQuick internals without having some baseline understanding of how everything works together. For the hour I spent browsing through the code, I still really don't have a clue as to , how the scenegraph backend is populated, how updates are done, when a property changes, how does the change get propagated to the backend, etc. Unless this stuff is documented on some level that is sufficient for an outsider to come in and understand how things work, I don't think it is possible for a "real" C++ API to be done by anybody other than the current QtQuick devs.
The second point that temp/Dian had made was that there is probably much cleaner ways of implementing the intended effect of "binding". That is, the intended effect of "binding" is that when some aspect of one element changes, it should impact another aspect in some functionally specified way.
Because I was clueless about the internal operations, I chose to look at the problem for now in a way that mirrors QML, where binds truely do update automatically. This is kind of like taking a sledgehammer to a problem requiring a regular hammer, but since I didn't have visibility into the QtQuick internals, it's what I chose to do for this example. I believe now, as Dian/temp had said, that there are very likely much cleaner ways to solve the problem if you look at the whole problem wholistically and design the underlying structures with the idea of having a C++ user API.
With that being said, let me first talk about the approach I took. Fundamentally, there's nothing in the QML declarative description except for binds that can't be easily mirrored in C++ in a straightforward way. The binds are a bit different. I considered two ways to mirror binds in C++. One, was something called "expression templates" (google it if you don't know what it is) and the second was C++11 lambda functions (although you can use any function object and not just lambdas). Although expression templates would have allowed for cleaner and easier to use binds, I decided to go with lambdas for this exercise. The reason I chose this was because lambdas (and function objects in general) can be just about anything that the user desires and they are directly supported in the language. Whereas for expression templates, I would have had to create some intricate libraries and these expression templates would only support the kinds of expressions I had the forsight to allow for.
Lambdas essentially allow you to create arbitrary functions to "bind" to a property. That is, instead of a value, a property can be a lambda function. Morever, you can embed this lambda function inside a "Property" object so that every time you access the object, you basically evaluate the function instead. But you still run into the problem of when to evaluate the lambda. Because I didn't really know how or when the property values are used in the backend code, I assumed, for the time being, that the backend should be notified whenever a property may be changed. That is, whenever the state of the property is "invalidated". Whenever a property is invalidated, then the backend can access the property again, thereby evaluating the underlying lambda function, and get back the "new" value of the function.
But in order to know when to re-evaluate the lambda, you have to know something about the functional dependencies of the lamda. For example, say you have a lambda like: &{return a+b;}, then you would want to re-evaluate the lamda whenever a or b have possibly changed. The problem is that for any arbitrary lambda, the compiler doesn't tell you what the functional dependencies are. The only way I'm aware of is for the user to supply the dependencies. And so this is the method I chose. To avoid the 6000 word limit, in the next post I will put the code that I wrote to support lambda-based properties with dependencies.
-
Below is the code that implements lambda-based Properties. After the definition of the Property<T> template, I have included an example "Rectangle" class that uses this Property<T> template. From this you can see the bind of the area is defined usinga lambda. Then, below that, I have a main program that uses an instance of the Rectangle and connects various signals so that you can see the effect of changing the width or height of the rectangle and that the area is recomputed whenever either width or height is changed.
I used gcc-4.7 to compile this:
g++ -std=c++11 -I$HOME/tools/boost/include -o dprop main.cpp
Since I transcribed this manually (don't ask...), I hope I didn't make too many typos.@
#include <iostream>
#include <functional>
#include <boost/signals2.hpp>using std::cout;
using std::endl;
using std::function;
using namespace boost::signals2;template<typename T>
class Property
{
public:
// Signals are based on boost::signals2 library
// This signal only is emitted only when the functor is evaluated and
// the actual value of the property has changed.
signal<void ()> changed;// This signal is emitted whenever the value of the property “might” be // changed. It indicates that the property should be accessed again to // get its updated value. signal<void ()> invalidated; // Basic constructor takes a functor as an argument to “bind” to the property Property(function<T()> evalFn) : m_prevValue() , m_evalFn(evalFn) , m_valid(false) { evaluate(); } // A constant value is translated to an lambda that returns that constant value // and then the above constructor is called. Calling a different constructor is a // C++11 feature. Property(const T & value) : Property([=](){ return value; }) { } // This takes an eval function and also an arbitrary list of Properties on which the // eval function depends. This is using the C++ variadic template feature. (notice the …) template<typename... Deps> Property(function<T()> evalFn, Deps &...deps) : Property(evalFn) { addDependencies(deps...); } // If this Property is already invalidated don’t do anything. Otherwise, set it to // not valid and emit the invalidated signal to propagate to all dependent properties. void invalidate() const { if (!m_valid) return; m_valid = false; invalidated(); } // The variadic template base-case. // Connect the invalidated signal in the antecedent property to this // property’s invalidate function. Property & addDependencies(Property<T> &p) { p.invalidated.connect([this](){this->invalidate();}); } // The variadic template recursive case template<typename P, typename... Deps> Property & addDependencies(P &dep, Deps &... deps) { addDependencies(dep); addDependencies(deps...); } // So that you can write: x = [](){ return foo; }; Property & operator=(function<T()> evalFn) { m_evalFn = evalFn; invalidate(); return *this; } // So that you can write: x = 234; Property & operator=(const T &value) { return *this = [=](){return value;}; } // if necessary, evaluate the function object and if it is different than // before, emit the changed signal and return true. If the newly evaluated // value is the same as before, do not emit invalidated signal and instead return false. bool evaluate() { if (m_valid) return false; m_valid = true; T newValue = m_evalFn(); if (!(newValue==m_prevValue)) { std::swap(newValue,m_prevValue); changed(); return true; } return false; } // conversion function so that you can access the property like a value. // e.g. : cout << x << endl; operator T() const { evaluate(); return m_prevValue; }
private:
mutable T m_prevValue;
function<T()> m_evalFn;
mutable bool m_valid;
};// Example Rectangle defined using Property<T> where the area Property is
// bound using a lambda. Notice that the dependencies are width and height
// properties.
struct Rectangle
{
Property<int> width;
Property<int> height;
Property<int> area;Rectangle(int w, int h) : width(w) , height(h) , area([=](){return width*height;}, width, height ) { }
};
// Example main program using the above Rectangle.
int main()
{
Rectangle r(100,200);r.area.changed.connect([](){ cout<<"area changed"<<endl; }); r.width.changed.connect([](){ cout<<"width changed"<<endl; }); r.height.changed.connect([](){ cout<<"height changed"<<endl; }); r.area.invalidated.connect( [&r]() { cout << "width="<<r.width<<" height="<<r.height<<" area="<<r.area<<endl; }); cout << "width="<<r.width<<" height="<<r.height<<" area="<<r.area<<endl; cout << "changing width" <<endl; r.width = 50; cout << "changing height" <<endl; r.height = 100;
}
@Since it is getting late here, I will post the QML and equivalent C++ code tomorrow.
-
bq. As I went through this exercise a lot of questions came up as to when exactly the bound properties are read and how they exactly are updated under the hood.
The bound properties must be QObject properties, declared with the Q_PROPERTY macro in C++, which means they typically have a getter, and usually also a setter and a getter if they're mutable. The Q_PROPERTY declaration makes Qt's meta object system aware of the properties, so that they among other can be found with a string lookup. Here's an example Q_PROPERTY statement from QQuickRectangle:
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
So when the QML binding like "x: parent.width / 2 - width / 2" is parsed, connections are automatically made to the properties' changed signals. The source properties for the binding are thus read first at the time the binding is first evaluated, and then whenever the change signal for one of the properties changes. One awesome feature of Qt signal / slot connections is that they work across threads, so you could have a C++ worker object in a separate thread exposed to QML, doing heavy computations in an asynchronous manner without interrupting the UI.
The connections are also automatically disconnected when either the receiving or sending object is destroyed. Whenever a property change leads to a visual change in a quick item it calls update() to signal to the quick canvas that it is dirty and thus the corresponding scene graph node / nodes might need updating before the next frame is rendered. The call to update() happens automatically in the backend (though you might need to call it manually if you implement a custom quick item).
Properties declared in QML automatically get the corresponding changed signal. Here's some more information on QML properties: http://doc.qt.nokia.com/qt5/qml-properties.html
I have to say your property binding system for C++ looks quite promising, gotta love C++11 lambdas. For the best interoperability with Qt I'd love to see it support Qt properties and signals though :)