C++ singleton interfacing with QML



  • I have a new problem but still associated with interaction between C++ and QML.

    I have a C++ class instantiated in main.cpp. I want all QML files to be able to access properties, invokable functions and receive signals from this class. I also want other C++ classes to do the same. This is a main class which forms a framework for my app and has qml files being show in a stackview. Think QML window with embedded qml viewport. This is using a model/view arch.

    So I initially, with help, added this class to qml context and made connection in qml.
    See https://forum.qt.io/topic/77216/receiving-c-signal-in-qml/5

    This allowed me to get signals from the class but I was not able to get invokable functions to work without generating warnings in debugger console BUT they did work. But my QList model stopped working. Seemed as if QList was no longer registered with QML.

    So I went down the path of creating a singleton class which is really how this main class/frame should work. Only 1 instance.

    Now I can access invokable functions without warnings, still my model is broken BUT now the slots in qml don't work again. Driving me crazy.

    Any help please!!

    //your code here
    ```main.cpp
    //SLY HERE
    // Second, define the singleton type provider function (callback).
    static QObject *QmlMainSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
    {
        Q_UNUSED(engine)
        Q_UNUSED(scriptEngine)
    
        QmlMain *qmlmain = new QmlMain();
        return qmlmain;
    }
    ...
        qmlRegisterSingletonType<QmlMain>("Main", 1, 0, "QmlMain", QmlMainSingletonProvider);
    
    ```main.qml
    import Main 1.0
    ...
        //slot for QmlMain
        Connections {
            target: QmlMain
    
               onBootStrapDone: {
                console.log("onBootStrapDone", QmlMain.KeyModel.target)
                QmlMain.cppNavSlot(QmlMain.KeyModel.target)
            }
    
            onKeyModelChanged:{  //only getting this after a click
                console.log("onKeyModelChanged", QmlMain.Title)
                console.log("onKeyModelChanged", QmlMain.Source)
    //            window.reloadModel =  true;
                drawer.close()
                titleLabel.text = QmlMain.Title
                stackView.replace(QmlMain.Source)
            }
        }
    ...
    //an invokable function in qmlmain.cpp
     text: QmlMain.mTr("#Help")
    

    //your code here



  • Looks like some mixup is happening. Slot in qml is not called means your qml main object did not emit appropriate signal. Can you confirm signal is generated from your QmlMain object ? How qlistmodel is related here. This confused me as well. May b u can split the question.



  • @sly110 I also feel there are several things going on here and you should break it into several questions and give a bit more information. If you have problems with invokables and warnings, tell what they are, what did you do, what did you expect, what did you get etc.

    If you want to have one object, coded in C++, created in C++, and use it in both C++ and QML, you should write a normal QObject derived class, instantiate it in C++, set it as context property for QML and start the QML engine and the application. Here is part of my code:

        QQmlApplicationEngine* engine{new QQmlApplicationEngine()};
        QQmlContext* ctx{engine->rootContext()};
        Connection* conn{new Connection()};
        CameraController* camera{new CameraController(conn)};
        qmlRegisterUncreatableType<CameraController>("eu.vpsystems.software", 1, 0, "CameraController", "Not to be created as QML type");
        ctx->setContextProperty("connection", conn);
        ctx->setContextProperty("camera", camera);
        engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
        app->exec();
    

    I have no problems with signals or slots in both ways or C++ properties or Q_INVOKABLEs. I use qmlRegisterUncreatableType because that class has enums which I want to use in QML. I also use models/views where model is based on QStandardItemModel, created in C++, set as a context property and used in QML.

    I don't know what a Qt singleton type is used for, but probably it isn't the right solution here. The singleton object is created by the QML engine and as far as I can see it's not meant to be used in C++. (About singletons in general, though not necessarily this Qt singleton, google for "why singleton is bad".) If you want only one object, create just one object. If it's created in C++ it can be used in QML after it has been set as context property.



  • All,
    Yes I am sorry for the mix up . Late and frustrated.

    Anyway I did a lot of refactoring based on the feedback and appear to have one glaring problem. Previously, my KeyData class was being instantiated without me knowing it by QML. Now the attempt is to instantiate from C++. I have a drawer control in QML that is a bunch of nav buttons. The model for this is in C++ as a QList of *KeyData.
    So my drawer is now unpopulated and in the debug window the error is:
    QMetaProperty::read: Unable to handle unregistered datatype 'QQmlListProperty<KeyData>' for property 'QmlMain::KeyModel'. Previously that was not an issue. I think a complication at least for me is that key and Nav are defined as structures and I've had to prepend 'struct' everywhere they were used.

    struct Key
    {
        QString id;
        QString type;
        QString title;
        QString icon;
        QString target;
    };
    
    struct Nav
    {
        QString id;
        QString title;
        QString source;
        QList <Key> keys;
        QList <int> milestones;
    };
    

    In my main class header that is now a exposed to QML as a context property, I have:

      Q_PROPERTY  (QQmlListProperty<KeyData> KeyModel READ readKeyModel NOTIFY keyModelChanged)
    ...
        //model for model/view of keys
        void writeKeyModel();
        QQmlListProperty<KeyData> readKeyModel();
    
        QList<KeyData *> getKeyModel() const;
        void setKeyModel(const QList<KeyData *> &keyModel);
    ...
        QList<KeyData *> _keyModel;
    

    In my main class imlementation I have:

    QList<KeyData *> QmlMain::getKeyModel() const
    {
        return _keyModel;
    }
    
    void QmlMain::setKeyModel(const QList<KeyData *> &keyModel)
    {
        _keyModel = keyModel;
    }
    
    
     QQmlListProperty<KeyData>  QmlMain::readKeyModel()
    {
        return QQmlListProperty<KeyData>(this, _keyModel);
    
    }
    
    
    void QmlMain::writeKeyModel()
    {
        _keyModel.clear();
    
        Key key;
        foreach (key, this->_navRecord.keys) {
              _keyModel.append(new KeyData(key.id, key.type, key.title, key.icon, key.target));
        }
    
        qDebug() << "setKeyModel";
    //    emit this->keyModelChanged();
    }
    

    In QML I have

        //nav drawer
        Drawer {
            id: drawer
            width: Math.min(window.width, window.height) / 3 * 2
            height: window.height
            background: Rectangle {  //add fill to drawer
                anchors.fill: parent
                color: "white"
            }
            ListView {
                id: listView
                currentIndex: -1
                anchors.fill: parent
                model: QML_MAIN.KeyModel     //c++ model
    

    Thank you for any help you are kind enough to provide


Log in to reply
 

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