Best way to share properties between C++ and Qml
-
Hello,
I'm working on an embedded project. The goal is to store all our user parameters in C++, but have them set in the UI designed in Qml/QtQuick. The selected method at the moment is to subclass QObject, and expose properties using Q_PROPERTY(prop READ WRITE NOTIFY). However, when the number of properties (variables in the C++ classes) gets large, the program gets verbose, with many accessor functions, signals, and macros.
Furthermore, maximum values, minimum values, and default values are specified in Q_ENUMS.
Thus, both an instance of a class and the class type are registered to Qml.
Is there a better way to do this?
Thanks for the help.
Brian -
You can lower the amount of things to write by using Q_PROPERTY(MEMBER) macro. "Link":http://qt-project.org/doc/qt-5/properties.html#requirements-for-declaring-properties.
-
Thanks for that.
In rereading the MEMBER property, I can still write a "set" accessor, which would check bounds, and not have to use a "get" accessor. That saves a bit of typing.
Still, I'm thinking there's got to be a less verbose way that I'm missing.
Maybe using Q_DECLARE_METATYPE or QVariantList, or something would be better? I have looked at them but see that I still have to define "roles," and the underlying code must look up the property by cross-referencing at least an index, if not a string or hash table. I'm concerned that will get slow in a hurry.
Brian
-
I might be missing something but how different would it be if you took QML out of the mix? Wouldn't you still want to write getters and setters for your private member variables. The only added verbosity is the Q_PROPERTY macro itself. Also I've never been a big fan of the MEMBER variable association. It seems to me it just makes your private member variables partially public. Somewhat misleading to have a "private" member variable that can be read and written outside the class. Seems like a maintenance nightmare for the unwary.
Also I like to use a form of the Presentation Manager design pattern and all this verbosity gets limited to a viewManager class that really does little else but expose functionality to QML.
-
Hello Rolias,
Thanks for the post. I'm not familiar with the Presentation Manager. I assume that is part of the pluralsite courses ...
Anyway, Maybe what I'm getting at is I'd really like to just create a struct (or something similar), and then pass copies or references of the struct around instead of individual parameters. That would be much less verbose, except to get that struct to QML, it takes a QVariant list of some sort, and then it has to have "roles," and then we're back in the same boat, or have I missed something?
Brian
-
Actually I should say Presentation Model, it's "Martin Fowler's design pattern":http://martinfowler.com/eaaDev/PresentationModel.html. But that's off topic.
A QVariantList can just be passed to QML and then used in QML as a JavaScript array. I'm not sure what you mean by "it has to have roles". Here's a quick example of C++ passing a QVariantList to a QML method.
@ QColor kermit = Qt::green;
QVariantList my_list;
my_list << 3.1415 << 30 << kermit << "guitars" ;
QMetaObject::invokeMethod(root,
"logArray",
Q_ARG(QVariant,
QVariant::fromValue(my_list)));@The function in QML might look like this
@ function logger(element)
{
print("Array element:"+element)
}function logArray(anArray) { anArray.forEach(logger) }@
Personally, I like this less than exposing the properties of a class and then just using those properties in QML. They don't need to be "passed" per se. Once setContextProperty() is used to let the QML know about the class the QML can just use any property with dot notation on the class (or struct) name that you gave it in the setContextProperty() call.
I guess this is more a design philosophy than anything. I tend to not use raw structures anymore even in my pure C++ code. I would rather control access to the underlying data through the class interface. This does mean more work for me when creating my classes but it tends to mean fewer bugs later on in the process. It also means I can change implementation details without breaking code that uses the class.
Also your original question implied you had to register the class type because you used enums, which is not true. (Assuming I understood what you meant). Using the Q_ENUMS macro of the enum type is enough that QML can then use it.
@ Q_ENUMS(Status)
Q_PROPERTY(Status status READ status CONSTANT)
enum Status{ Newbie, Learning, QtWizard};@If you just want to exchange data creating an instance of your class in C++ and using setContextProperty on that instance is enough. BUT, AFAIK, you do have to decorate your class with Q_PROPERTY, Q_ENUM, Q_INVOKE and signals and slot syntax. The MOC needs some mechanism for knowing what data it needs to "translate" for the Qt Quick environment.
-
Hello Rolias,
Thanks for the tips and experience. At this point, it looks like it is 6 of one, and half a dozen of the other. Your case is well-stated, and I'll stick with the model I've been using. Reassuring to know I'm on the right path.
Brian
-
Take it as just one (old) developer's opinion. Leave the topic open and maybe you'll get some others. Your post did get me thinking however. I don't see why the Q_PROPERTY macro couldn't be "improved" with an AUTO attribute for trivial getters and setters. Something like
@Q_PROPERTY ( QString propName READ WRITE AUTO)@Then this would create behind the scenes everything needed. The member variable, signal, getter and setter. Sort of like the current refactoring does only none of the code would be visible in your file. Sort of like the Qt Widget UI code works.
In the future if you ever needed to change the implementation details you could just remove AUTO and flesh out everything yourself (using that handy refactoring to do it) and nothing would change for users of your class.
-
[quote author="Rolias" date="1414775263"]I don't see why the Q_PROPERTY macro couldn't be "improved" with an AUTO attribute for trivial getters and setters. Something like
@Q_PROPERTY ( QString propName READ WRITE AUTO)@[/quote]When I first saw MEMBER being mentioned on Qt mailing list, before it was integrated, I actually thought this is how it is going to work. That indeed would make development much faster, and the code cleaner. There is really no reason to waste time on writing dumb getters and setters when a macro/ code generator can do it for you in a few miliseconds during compilation.
-
EDIT : I removed the bulk of this post - MEMBER only works seamlessly for string types. I'm thinking my own AUTO_PROPERTY macro is the way to go for what I want.
A member of the Qt team who was not aware of this thread (AFAIK) pointed me to the possiblity of writing a custom macro and "gave this example":http://code.woboq.org/qt5/qtdeclarative/src/quick/items/qquickaccessibleattached_p.h.html#58 which I think QtIs2Cool might find interesting since it uses a struct and gathers a whole bunch of properties behind the struct.
-
In case anyone is interested I wrote up a blog with links on Gist to the macros. With these macros you can create a property with
AUTO_PROPERTY(QString, myProperty)
The macro will create the Q_PROPERTY, the getter, setter, signal and member variable. Since I'm a macro newbie any and all (polite) feedback will be appreciated. -
[quote author="Rolias" date="1415131843"]In case anyone is interested I wrote up a blog with links on Gist to the macros. With these macros you can create a property with
AUTO_PROPERTY(QString, myProperty)
The macro will create the Q_PROPERTY, the getter, setter, signal and member variable. Since I'm a macro newbie any and all (polite) feedback will be appreciated.
[/quote]Woah, that is great!
-
Thanks for the help. Many good ideas here.