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 using QObject::findChild(). Jesus, this is just so ugly code... For the same reason I dislike using QQmlContext::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 file main.qml which communicates with a Gui C++ class by instantiating it and calling Q_INVOKABLE methods and gets responses by handling emitted signals from Gui. Now I also have a ListView in QML, which has a model residing in C++ as a QAbstractListModel 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 by Gui isn't aware of any QML, because I want to abstract the Gui 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 me qmlRegisterType 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! :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.