Solved Complex Qml-C++ communication scenario
-
Hey,
this is not the usual question how to communicate between QML and C++, I already know that. It's rather a question of how to deal with the Model-View-Delegate paradigm, when the model is in C++, the View and delegate in QML and the users in potentially both QML and C++.
So I tried all the possible methods to communicate between C++ and QML and applied them on one of my projects. I think that the following methods scale the best when working on real projects: QML communicates to C++ by instantiating a C++ object and calling its
Q_INVOKABLE
methods. C++ communicates to QML by emitting signals on an object instantiated by QML, which then are handled by QML.The reason that I am telling this, is that I rather don't want to see hacky things in my code such as using
QMetaObject::invokeMethod()
on an object found by usingQObject::findChild()
. Jesus, this is just so ugly code... For the same reason I dislike usingQQmlContext::setContextProperty()
. It just is kinda hacky...Now since there also exist my previously stated favorable methods, this is usually not a problem. However, consider the following program layout:
I have a QML filemain.qml
which communicates with aGui
C++ class by instantiating it and callingQ_INVOKABLE
methods and gets responses by handling emitted signals fromGui
. Now I also have aListView
in QML, which has a model residing in C++ as aQAbstractListModel
derived class. As a standalone this works perfectly: The QML instantiates such a model and can bidirectionally communicate with it. However, now the problem: Another class, which is instantiated byGui
isn't aware of any QML, because I want to abstract theGui
layer in order to make future refactoring as easy as possible. But this class has to communicate with this particular model, which was instantiated by QML. Of course I can include the model header and create my own instances, but I have to communicate with this particular object.The way I see it, there is no clean way to do this in Qt. So basically what I am asking is: What is the cleanest approach to do this in Qt, which also scales well to future refactoring and doesn't require me throwing away my
Gui
abstraction layer.P.S.: I already tried the hacky solution of using a static singleton model and the QML instantiated one would delegate events to the static one which is accessible by other C++ classes. This however introduces the problem that the static object cannot communicate with the instantiated one, only the other way around.
-
I don't think using
setContextProperty
is hacky. If you have an object that is part of your business layer, don't instantiate it in the UI Layer.
For meqmlRegisterType
should be used when you want to extend QML features with a new type, not for exposing your business layer with a type that can be used only in a single predetermined way.Using
QObject::findChild()
is bad practice since it couples your c++ code to your QML code,QQmlContext::setContextProperty()
doesn't. -
Thanks, I will try it this way then.
The thing that I dislike about this option, is that you are not really aware in QML that this property is defined, which introduces problems, for example you could forget that this property was defined from C++ and redefine it with the same name in QML. -
@Magnus21 You can have one container object called "backend" and other needed objects as its properties. Then you would use backend.actualWorkObject.myMethod() etc. Or you can name you objects like backend_myObject etc.
-
@Eeli-K said in Complex Qml-C++ communication scenario:
@Magnus21 You can have one container object called "backend" and other needed objects as its properties. Then you would use backend.actualWorkObject.myMethod() etc. Or you can name you objects like backend_myObject etc.
Taking this a step further, a backend communcation QML component can proxy access to a C++ singleton object while providing the usual QML object id conventions.
extern static QObject *singletonObject; class BackendConnector : public QObject { ... BackendConnector() { connect(singletonObject, &SingletonType::boolChanged, this, &BackendConnector::boolChanged); } Q_PROPERTY(bool backendBool READ getBool NOTIFY boolChanged) bool getBool() { return singletonObject->getBool(); } signals: void boolChanged(bool); ... };
Item { BackendConnector { id: connector } Text { text: connector.backendBool } }
Replace the static extern singleton object with the object resolution mechanism of choice. Return the object as a property if the overhead of a chained property getter, setter, or signal emission isn't acceptable.
-
Thanks, this really helps! :)