Does Qt need a modern C++ GUI API?
-
bq. Simply exposing the C++ interface of all QML elements won’t buy you anything, as the most important feature of Qt Quick (bindings) still can’t be handled that way
Can someone please explain why the above statement is true (or not)?
-
@broadpeak: My point was, agreeing with mlong, that the poll isn't even closely neutral enough.
Just to clarify on my views... Although I am generally fond of native code, I believe QML is a good approach to UI. But seeing the discussion, "who can shout the loudest" still comes to mind and, respectfully, I will go back to my work.
-
With regards to "who can shout the loudest". What other choice does one have at this stage if one isn't able to "do it yourself" and there is so much inertia and false information in the system due to years to promulgating the "goodness" of QML and years of developing QML by dozens of engineers.
-
The one thing I honestly don't get is why are some people acting like I am the devil's advocate and that I am standing for some evil cause what will somehow be harmful to people?
Since when is the option of sticking to C++ and offering your users the most efficient solution at the expense of a little extra work a bad thing?
Can somebody perhaps answer?
-
[quote author="c++freeloader" date="1335165426"]
For example, perhaps, some set of people might be interested in putting together C++ classes that mirror the currently published QML elements. The problem is that at least for some of these, it is difficult to know where to even start. If one of the current developers could perhaps create an example of what a C++ API class could look like and an explanation of how it interfaces to the lower level API's then some volunteers could get started looking at this example and actually make some progress.
[/quote]Seems both you and temp ignored my previous comment, where I pointed to the C++ API exposing the QtQuick items: http://qt-project.org/forums/viewreply/83016/
-
And for further detail you might be interested in Alan's comments on the qt development mailing list: http://lists.qt-project.org/pipermail/development/2012-April/003413.html
I think it's a better idea to discuss there, as the discussion will reach more of the developers behind scene graph / qt quick / qml.
-
capisce - Well, this is exactly what the whole point is about - tailoring a nice public API. So a Qt engineer managed to come up with some ugly code to make a biased example of how tedious would it be to use Qt Quick with C++, what a surprise!!!
@QQuickCanvas canvas;
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; Rectangle{}");
QQuickItem* item = qobject_cast<QQuickItem*>(component.createObject());
item->setX(10);
item->setY(10);
item->setWidth(10);
item->setHeight(10);
item->setParent(canvas);
canvas.show();@What about this:
@#include <QtQuick2>
QQuickApplication myApp; // contains engine and surface
QQRectangle item(10, 10, &myApp); // avoid three lines by using that amazing C++ feature, the constructor
item.moveTo(10, 10);myApp.exec(); // shows the entire hierarchy@
And it is not like a contemporary compiler stack cannot hold at least half a megabyte of objects, usually quite a lot more, but feel free to instantiate the item dynamically if you care that much about the significant overhead of dynamic memory allocation:
@QQRectangle *item = new QQRectangle(10, 10, 10, 10 &myApp); // define both dimensions and location in the constructor, why not?@
Although I don't really think this is nessesary, as the class should be fairly small footprint, and resources such as its image cache are supposed to be internally allocated dynamically anyway, since they vary.
I do realize you can make it ugly and you do realize it can also be made too nice for many people to even bother with QML and company, so WTH?
A nice, intuitive public API can reduce that code quite a bit, so instead of those ugly and unnecessary 11 lines we end up with something neat like:
@#include <QtQuick2>
QQuickApplication myApp;
QQRectangle item(10, 10, 10, 10, &myApp);
myApp.exec();@And no, it is not my responsibility, neither is it in my capacity to walk after the entire task force behind the project and make nice public APIs, unlike me you get paid to do that kind of stuff. As I said on numerous occasions, it will take me more time to do it from scratch that patching your messy, undocumented private API. You designed it, you know it, for you and your team it will be tremendously easier to do it, you get paid to do it, you chose not do it the right way right from the start and instead center it all around QML, it does appear to be your responsibility, I am not asking you to come over and do my job, so why do you expect that of me?
As I already said, if you have that much problem designing a neat public API, I am offering my assistance totally free of charge for the collective benefit of all Qt developers, I give you a design for an elegant C++ frontend to Qt Quick, you do the heavy lifting and hide that ugly code in a nice, neat wrapper. Qt Quick gets more public, everyone is happy.
-
Although I dislike temp's provocative style of writing (no offense meant, just my reception) - he (or she? don't know) has a point. I think QML is in fact a very nice additional feature to Qt. I can think of a lot of cases when I would rely on a technology like QML (e.g. it is very easy for a design team to create mock-ups with QML that may be evolved into fully functional UIs) but:
If you provide such a nice and seemingly natively supported feature like QML there should also be an easy way to access these features without actually using a new language but from within the already existing API. There are many development teams out there without the capacity (money or time) to invest into training for QML.
Regarding the "it's open source - so just contribute"-point I saw mentioned in this thread: Just because you may contribute content to a repository it does not mean somebody with a good ideas has the required abilities/time/... to do so :)
-
bq. As I said on numerous occasions, it will take me more time to do it from scratch that patching your messy, undocumented private API.
Since you again ignored the part of my earlier that pointed you at the current private API, here's a direct link: http://qt.gitorious.org/qt/qtdeclarative/blobs/master/src/quick/items/qquickrectangle_p.h
It won't be made public for Qt 5.0, but nothing's stopping you from trying out that API, or writing an add-on that exposes it. Just be aware that it comes without SC / BC guarantees.
Alan on the other hand was offering an alternative that would be SC / BC, and of course it's a bit more tedious, but you could in theory write your own C++ wrapper around it, exposing it with a nicer API. Just because Qt doesn't offer everything straight away doesn't keep you from writing a little bit of extra wrappers in the short run.
-
[quote author="Daniel Eder" date="1335181849"]Regarding the "it's open source - so just contribute"-point I saw mentioned in this thread: Just because you may contribute content to a repository it does not mean somebody with a good ideas has the required abilities/time/... to do so :)[/quote]
If nobody has the required abilities and time to do so, then it won't happen. In fact, it's not about the abilities or the time. It's about the financial investment of developing and maintaining the Qt codebase. And frankly, I don't really understand what we're hoping to achieve here.
Even with open source, there's no free lunch as they say, and Qt developers are only that, paid developers. If none of the major contributing employers (Nokia for the most part, but also Digia, KDAB, Accenture, Intel...) judge it interesting to invest in a C++ API, then it's indeed unlikely to become a priority anytime soon.
It's a meritocracy where Nokia happens to be the party investing the most, but luckily, it's no longer a closed group. So if there is another company out there that wants to pay several full-time devs to work their way up to a contribution point where they are part of the decisional level of Qt, then what's stopping them? Just don't expect other companies to pay for something that has obviously been judged not a priority for them. I know it sounds a little cold, but I think that's really all there is to it. -
[quote author="c++freeloader" date="1335171302"]bq. Simply exposing the C++ interface of all QML elements won’t buy you anything, as the most important feature of Qt Quick (bindings) still can’t be handled that way
Can someone please explain why the above statement is true (or not)?[/quote]
It is true. Well, at least most of it. The thing is that normally you have an object with some property, and you can bind that object's value to another value in some overloaded method, like the paint event. This is somewhat tedious and has to be specific for every different scenario. And in QML you are isolated from all this, unless you dig into creating a custom object in C++, and this is where JS comes in handy for some wiring of properties.
That being said, there are alternatives to that in C++, and while they have their overheads, those are minimal compared to the overhead of having a running JS VM and JS execution.
One solution that comes to mind is instead of storing the value of a property, the object stores an address of a property, so binding to another object property becomes fairly simple, but in case you want a regular property you need to instantiate that type and pass it to the object, effectively binding to another object again.
You can also have a bool whether a property is set or bound, and a union that can either hold an address of a property in the case of bound or the actual property in the case of set. You can use bit packing to minimize the space used by all those bools for every property.
You can also instantiate a binder object (can even be a part of the QQApplication too) which may take any number of bindings between objects and properties in any order you want to, it will not be automatic as it currently happens in QML with JS but it is not that much of a pain too, and its overhead will once again be minimal compared to the overhead QML induces.
Last and least, as I mentioned, you can have an empty virtual method like for example bindEvent, which you overload and add your bindings there, but in this case you have to crawl up the hierarchy and shoot in the blind to connect to an object you know is there. That is why I chose this method last.
There might be other ways to do it as well, who knows, those are just a few that come immediately to mind. The main hardship with doing property binding in C++ is you have to think a little, which is not that bad at all if you ask me. And it is not always a good thing to relieve people of thinking, just look at the disaster the relieving of physical activity has done to the world - an epidemic of overweight and obesity. Believe it or not, this happens to the mind too. That is partially why people coming from other languages find C++ such a pain. It is like making a fat guy run for his meal :D He won't like it despite the fact it is actually good for him.
-
I just would like to throw my 2 cents in:
If there were a C++ API as alternative to QML then one could write a code generator/compiler that translates QML code into native C++ code that uses this API. This would let you profit from the advantages of both worlds:
- Code can easily be maintained by editing QML files.
- During development you can profit from the fact that you don’t need to recompile after every change you make.
- The compiled C++ code obviously is more efficient.
- API changes wouldn't be as dramatic because the code generator could be used to recompile the sources incorporating the API changes.
- You can protect your IP, because the released software won’t include QML code (be it an embedded resource or not).
While I am not as concerend about the efficiency gain (or drawback) I am concerned about being able to protect IP, and this is my reason why I would appreciate a C++ interface plus such a compiler.
-
The way I think it is best used and why it doesn't need a pure C++ API:
C++:
- The application's shell
- Custom QQuickItems for application components needing performance
- Heavy parts of the application's logic exposed as slots through QML plugins/global objects
QML:
- UI layouts
- Light parts of the application's logic
I think that a pure C++ API can only be slightly more efficient for most use case, and I think that the maintenance and complexity costs of both a QML and C++ binding systems would not be worth paying for those benefits.
As much as I like signals and slots, I don't want to have to write a slot for every single connection between Rectangles/MouseAreas in the app that involve more logic than a direct value binding.
If a component of an application really requires ultimate efficiency, it is then better to go directly down to the metal with the current scene graph API to avoid as much abstraction as possible.The real question to be polled should be "Should we expose QQuick* and QSG* classes in the public API?". And for the sake of the quality of Qt5's future API, the answer should remain "no" at least until 5.1 or 5.2.
-
If you assume for the moment that there is a way to deal with binary compatibility, then I think that the arguments typically boil down to one of more of:
- QML is just so much more readable and easier to use that I can't imagine anyone ever wanting to use C++
- You can't easily do things in C++ that you can do easily in QML
- You can't do declarative in C++, because C++ is a imperative language
My concern is that people really aren't understanding what a well defined C++ interface would allow and are thus rushing to an incorrect judgement on items 1-3 above. To better illustrate the kinds of things that could be, I took a segment of QML from the Qt4.8 documentation that has animations and states and converted it to a hypothetical C++ API based description. These are shown below. From this, you can see that the C++ version is not necessarily overly verbose or less understandable than the QML version.
Note the C++ version implicitly uses various recent C++ language features to simplify and streamline the resulting code. This illustrates another reason why C++ is better in general than QML - that is, C++ has a myriad of language-based that have been intensively developed and refined over 30 years, culminating in C++11 which is supported by all major compilers now. Language design is notoriously difficult to do right and QML cannot hope to compete with C++ in this area.
C++ also enables a use of a wide variety of libraries that can be incorporated into the UI design if desired. These libraries include the C++ std library as well as Boost and many others. QML also cannot hope to compete with C++ in the area of available libraries that can be leveraged if desired. Now you may say, "well, for UI I don't need any libraries, so that doesn't matter". The response to this is, "maybe you can't imagine using any libraries, but that doesn't mean that someone else doesn't have a need to use some crazy library." For example, you might want to use some graph library (of which there are many available) to help create your UI. But with QML, this becomes a lot more tedious to do.
Now you might argue that a "designer" isn't going to use C++, to which I would probably say, yes, you're right, but they're probably not going to use QML directly either. They're more likely to use a tool like QtCreator and a such a visual design tool doesn't inherently require QML (e.g. QtDesigner doesn't use QML).
@====================== QML version =================
import QtQuick 1.0Rectangle {
width: 400
height: 400Rectangle { id: coloredRect width: 100 height: 100 anchors.centerIn: parent Behavior on color { ColorAnimation {} } MouseArea { id: mouser anchors.fill: parent hoverEnabled: true } states: [ State { name: "GreenState" when: mouser.containsMouse PropertyChanges { target: coloredRect color: "green" } }, State { name: "RedState" when: !mouser.containsMouse PropertyChanges { target: coloredRect color: "red" } }] }
}
====================== C++11 version =================
#include <QtQuick2>class OuterRect : Rectangle {
struct InnerRect : Rectangle {
MouseArea mouser;
slist<State> states;InnerRect(Rectangle *parent) : Rectangle(100,100), mouser(this) { colorChanged().connect(&ColorAnimation); anchors().centerIn(parent); mouser.anchors().fill(this); mouser.hover_enabled(true); states = { { "GreenState", mouser.containsMouse, { this.color, "green" } }, { "RedState", unary_negate(mouser.containsMouse), { this.color, "red" } } }; } } coloredRect ; OuterRect() : coloredRect(this), Rectangle(400, 400) {}
};
@ -
I agree it's beautiful, and nobody is against a pure C++ API.
It is just usually smart to offer and support only one solution for a given problem, and offering only a C++-only API wouldn't be as good as what QML has to offer to reduce application development costs.
QML has both pros and cons compared to C++, and it's part of the reasons why the Qt Quick scene graph isn't tightly coupled with QML/QtDeclarative. JavaScript isn't mandatory to be able to use the new graphics pipeline.
So overall something had to be chosen to be shipped with Qt 5.0 to offer a complete solution, and it doesn't imply that this will be the de facto solution for all 5.x releases.
-
[quote author="c++freeloader" date="1335227533"]From this, you can see that the C++ version is not necessarily overly verbose or less understandable than the QML version.[/quote]
I disagree, I think the QML version is much easier to read/understand (and to write) than the hypothetical C++ version.
In the C++ version, you need to deal with low-level concepts like variable declarations vs definitions, constructors, initialization lists, pointers, class definition vs instance, signals & slots, etc.
Not that a Qt developer would have a problem to understand any of those, but interpreting foreign code that has all those concepts tightly thrown together takes a lot more mental effort than understanding a declarative object tree consisting of clean, uniform statements of one of the forms
@ Element { ... }
property: ...
property: [..., ..., ... ...]@
...for which the parser will perform all the necessary "instantiating" and "connecting" automatically.C++ just isn't optimized as a clean declarative definition language for in-place defined nested objects. The model of... 1) class definition 1a) member declarations 1b) constructor for member definitions 2) class instantiation ...is inconvenient for such a straight-forward declarative use-case and puts information that belongs together in separate places (which, if several levels of nested objects are defined, can become quite far apart for the outer objects).
Also, the "streamlining" (as you call it) which you demonstrate e.g. for defining a state as...
@{ "GreenState", mouser.containsMouse, { this.color, "green" } }@
...looses the readability advantage of QML's "named property statements" idiom and doesn't really seem to appreciate the intended flexibility of such objects (most properties of most elements are optional, most elements allow child-elements, it should be easy to add more properties or potential child-elements in future versions).As for practicality of implementation, I haven't dug too deep into the possibilities of C++11 yet, so I might be overlooking a few things that could be handled behind the scenes in your example, but I'm wondering...
In the C++ version, how would the Qt Quick engine know when to re-calculate the value of "mouser.containsMouse" and "unary_negate(mouser.containsMouse)"?As for the usefulness, how much would really be gained? A runtime cost would still incur during loading of the Qt Quick scene.
As I understand it, what the Qt Quick engine does is:Translate QML input into an optimized internal object tree.
Run the Qt Quick scene according to those object definitions.
With you C++ API, it would become:
Translate C++ object tree input into an optimized internal object tree. (And no, I don't think letting the two be the same is feasible.)
Run the Qt Quick scene according to those object definitions.
Regarding verbosity, you cheated a little by using a pretty concise coding style for the C++ example, but a line-number wasteful coding style for the QML version.
This is how the QML example would look with a coding style that's similarly-concise as the one you used for the C++ example:@import QtQuick 1.0
Rectangle {
width: 400; height: 400Rectangle {
id: coloredRect
width: 100; height: 100
anchors.centerIn: parentMouseArea { id: mouser; anchors.fill: parent; hoverEnabled: true } states: [ State { name: "GreenState"; when: mouser.containsMouse PropertyChanges { target: coloredRect; color: "green" } }, State { name: "RedState"; when: !mouser.containsMouse PropertyChanges { target: coloredRect; color: "red" } } ] Behavior on color { ColorAnimation {} }
}
}@ -
And a point about the run-time cost of parsing / translating of QML, the same thing exists in OpenGL, with GLSL snippets needing to be compiled into GPU-specific internal shading language by the driver's built-in compiler. Still, the result is high performance 2D / 3D graphics, and not that many people are complaining about it.
-
[quote author="jdavet" date="1335386303"]
I disagree, I think the QML version is much easier to read/understand (and to write) than the hypothetical C++ version.[/quote]
"easier" - maybe, though that I guess depends on personal preferences, how much prior experience with QML, degree of comfort with JS, how much you understand the object model, and so on. "Much easier" - that is hard to argue. And I wasn't arguing that QML is much easier to understand, I was arguing, "... not necessarily overly verbose nor less understandable than QML".
[quote author="jdavet" date="1335386303"]In the C++ version, you need to deal with low-level concepts like variable declarations, constructors, initialization lists, pointers, class definition vs instance, signals & slots, etc.[/quote]
As far as I can tell, the property statements are not too dissimilar from "variable declarations". A property that already exists in a super-class doesn't need to be declared either. i.e. if you just want to assign it, you can do that. Moreover, if you want, using C++11, you can put the initialization directly at the point of declaration. e.g.
@ struct Foo {
int width = 100; int height=200;
...
};@If you're worried about having to declare the "type" of the variable, this is only necessary for newly defined properties and you can argue that static typing has some advantages and some disadvantages to dynamic typing (which is what QML in effect uses). You can always use "auto" to avoid having to think too hard about the exact type you want. Admittedly, having to specify a type or "auto" does add one more word to the description - hardly making QML "much easier" to understand.
I'm not sure how you're criticizing "initialization lists", when after all, QML has essentially the same thing, though again, much less flexible. You don't after all have to use initialization lists. Because it's C++, there are many different ways to do it, depending on the specific needs at the time. For example, say you had an arbitrary number of items to set up and each had to have some specific computation to compute location, color, size, etc. To do this in QML, now you're forced to jump through hoops (which makes it harder to understand BTW). With C++ you can do it however you want.
It's hard to argue that a class definition and instances are all that much harder to use than a purely instance based thing. The programming world (as well as many other disciplines) has pretty much settled on the concept of definitions and instances, because it allows easy reuse. You can argue that the rest of the world is stupid, but that's a pretty hard argument to win.
"signals and slots" - again, I find myself wondering what you're talking about - it's not as though in QML you don't have to have signals and slots. In QML, you have to put them in using "when: <signal>" - not too much different than saying "<signal>.connect(functor)". And you can use C++11 lambda's to allow in-place definition of the slot. Or you can do pretty much anything else to your heart's content. The point is, it's flexible to suit the needs of everybody. And all this flexibility works out of the box. Don't need to spend a dozen or more Nokia engineers to reinvent the wheel when something already exists that works well - as so many are so fond of repeating ad-nauseum when it comes to implementing a C++ API. (Sounds a bit hypocritical, but hey, that's just me)
(continued...)
-
(continuation from previous post...)
[quote author="jdavet" date="1335386303"]
Not that a Qt developer would have a problem to understand any of those, but interpreting foreign code that has all those concepts thrown together takes a lot more mental effort than understanding a declarative object tree consisting of clean, uniform statements of one of the formsC++ just isn't optimized as a clean declarative definition language for in-place defined nested objects. The model of... 1) class definition 1a) member declarations 1b) constructor for member definitions 2) class instantiation ...is inconvenient for such a straight-forward declarative use-case and puts information that belongs together in separate places (which, if several levels of nested objects are defined, can become quite far apart for the outer objects).[/quote]
You can make a syntax as clean and simple as you want by further restricting the use cases for the syntax. If I develop a language to describe circles, I can just use a single number - the radius. The problem comes when you actually want to live in reality. You'll have a hard time convincing anybody that QML has been sufficiently used on enough "real" non-toy projects to know that it has sufficient syntax to support all reasonable use-cases. Especially when there is not a single example that I'm aware of where QML has been used to develop non-trivial applications. If you have an example, please post so I can be impressed. C++ has been used on an uncountable number of "real" applications. It's been in development over 30 years. Naturally, the syntax and language facilities have grown to support "real world" applications. The point again is even with all this extra flexibility and support for real world apps that, "... not necessarily overly verbose nor less understandable than QML".
[quote author="jdavet" date="1335386303"]
Also, the "streamlining" (as you call it) which you demonstrate e.g. for defining a state as...
@{ "GreenState", mouser.containsMouse, { this.color, "green" } }@
...looses the readability advantage of QML's "named property statements" idiom and doesn't really seem to appreciate the intended flexibility of such objects (most properties of most elements are optional, most elements allow child-elements, it should be easy to add more properties or potential child-elements in future versions).[/quote]
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 author="jdavet" date="1335386303"]
As for practicality of implementation, I haven't dug too deep into the possibilities of C++11 yet, so I might be overlooking a few things that could be handled behind the scenes in your example, but I'm wondering...
In the C++ version, how would the Qt Quick engine know when to re-check the value "mouser.containsMouse" ?[/quote]Just like any other signal somehow "knows" when it's been emitted. Something in the implementation does the emitting.
[quote author="jdavet" date="1335386303"]
As for the usefulness, how much would really be gained? A runtime cost would still incur during loading of the Qt Quick scene.
As I understand it, what the Qt Quick engine does is:translate QML input into an optimized internal linked object tree in memory
run the Qt Quick scene according to those object definitions
With you C++ API, it would become:
translate C++ object input into an optimized internal linked object tree in memory
run the Qt Quick scene according to those object definitions
[/quote]
They are both declarative after all. Something must translate from the declarative description into something that is processable by the CPU/GPU.[quote author="jdavet" date="1335386303"]
Regarding verbosity, you cheated a little by using a pretty concise coding style for the C++ example, but a line-number wasteful coding style for the QML version.
This is how the QML example would look with a coding style that's similarly-concise as the one you used for the C++ example:@
import QtQuick 1.0Rectangle {
width: 400; height: 400Rectangle {
id: coloredRect
width: 100; height: 100
anchors.centerIn: parentMouseArea { id: mouser; anchors.fill: parent; hoverEnabled: true } states: [ State { name: "GreenState"; when: mouser.containsMouse PropertyChanges { target: coloredRect; color: "green" } }, State { name: "RedState"; when: !mouser.containsMouse PropertyChanges { target: coloredRect; color: "red" } } ] Behavior on color { ColorAnimation {} }
}
}@
[/quote]I'd hardly call it cheating - I just copied and pasted from the docs - written presumably by the QML developers themselves. If anything, what you wrote looks even more similar to the C++ version I provided - further evidence that C++ provides nearly all the readability and succinctness of QML. At the same time, C++ provides dozens of other significant advantages. Since these have been stated so many other times, I will not repeat them here.