Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to Invoking QML Methods with c++ and connect(..,signal,...,slot) ?



  • i want to invoke qml function with c++ and use the signal and slot(QObject::connect).
    i don't want to invokemethod(https://doc.qt.io/qt-5/qtqml-cppintegration-interactqmlfromcpp.html).
    don't use : qqmlengine / QMetaObject::invokeMethod
    only, i use qquickview/connect ...
    please help me.

    Here is the code that I wrote. I think that this is normal working.... why don't work...?
    this code works : qml->cpp.
    This code does not work : cpp->qml

    touch next -> controlEvent in QmltoCppControl.cpp -> case 10: -> nextScreen -> emit SendMsgtoQml_ChangeScreen((QVariant)varData); in QmltoCppControl::nextScreen
    -> and function onChangeScreenQml(msg) in main.qml
    but, doesn't work function onChangeScreenQml(msg) in main.qml

    what is problem... ??
    connect or ... ???

    main.cpp

    #include <QGuiApplication>
    //#include <QQmlApplicationEngine>
    //#include <QApplication>
    
    #include <QQuickView>
    
    #include <QtQml>
    #include <QObject>
    #include "QmltoCppControl.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQuickView view;
        QmltoCppControl qmltocppcontrol;
        qmlRegisterType<QmltoCppControl>("QmltoCppControl", 1, 0, "QmltoCppControl");
        view.setSource(QUrl("qrc:/main.qml"));
        QObject *qmlRootObject = (QObject*)view.rootObject();
        QObject::connect(&qmltocppcontrol,SIGNAL(SendMsgtoQml_ChangeScreen(QVariant)), qmlRootObject,SLOT(onChangeScreenQml(QVariant)),Qt::QueuedConnection);
    
        //view.rootContext()->setContextProperty("QmltoCppControl", &qmltocppcontrol);
        view.show();
    
        return app.exec();
    }
    
    

    QmltoCppControl.h

    #ifndef QMLTOCPPCONTROL_H
    #define QMLTOCPPCONTROL_H
    
    #include <QObject>
    #include <iostream>
    #include <QVariant>
    
    class QmltoCppControl: public QObject
    {
        Q_OBJECT
    public:
        explicit QmltoCppControl(QObject *parent = 0);
    
    public:
        Q_INVOKABLE void controlEvent(int id);
    signals:
        void SendMsgtoQml_ChangeScreen(QVariant varData);
    private:
        void exitApp();
        void nextScreen();
    };
    
    #endif // QMLTOCPPCONTROL_H
    
    
    

    QmltoCppControl.cpp

    #include "qmltocppcontrol.h"
    #include "controleventid.h"
    
    QmltoCppControl::QmltoCppControl(QObject *parent)
        :QObject(parent)
    {
    }
    
    void QmltoCppControl::controlEvent(int id)
    {
        std::cout << "void QmltoCppControl::controlEvent id :: "<< id << std::endl;
        switch(id){
        case 0:
        {
            exitApp();
            break;
        }
        case 10:
        {
            testnum++;
            nextScreen();
            break;
        }
        default:
        {
    
        }
        }
    }
    
    void QmltoCppControl::nextScreen()
    {
        std::cout << "void QmltoCppControl::nextScreen() :: "<< std::endl;
        QVariantList varData;
        varData.clear();
        varData << 1;
        emit SendMsgtoQml_ChangeScreen((QVariant)varData);
    }
    
    

    main.qml

    import QtQuick 2.9
    import QmltoCppControl 1.0
    
    Rectangle {
        id: main_rect_id
        x: 0
        y: 0
        width: 1920
        height: 560
        visible: true
    
        property int nScreenId: 0
    
        function onChangeScreenQml(msg) {
            console.log("onChangeScreenQml\n");
            qmltocpp_id.controlEvent(0);    //0 = EVENT_APP_EXIT
        }
    
        QmltoCppControl {
            id: qmltocpp_id
        }
    
        Text {
            id: textid;
            font.pixelSize: 30
            horizontalAlignment: Text.AlignHCenter
            text: qsTr("Hello World")
            anchors.centerIn: parent
        }
    
        Item {
            id: next_item_id
            x: main_rect_id.width/4
            y: 460
            width: main_rect_id.width/4
            height: 100
    
            Text {
                id: next_text_id
                font.pixelSize: 30
                horizontalAlignment: Text.AlignHCenter
                text: qsTr("NEXT")
                anchors.centerIn: parent
            }
    
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    qmltocpp_id.controlEvent(10);    //10 = EVENT_SCREEN_NEXT
                }
            }
    
        }
        Item {
            id: exit_item_id
            x: main_rect_id.width/4*3
            y: 460
            width: main_rect_id.width/4
            height: 100
    
            Text {
                id: exit_text_id
                font.pixelSize: 30
                horizontalAlignment: Text.AlignHCenter
                text: qsTr("EXIT")
                anchors.centerIn: parent
            }
    
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    qmltocpp_id.controlEvent(0);    //0 = EVENT_APP_EXIT
                }
            }
    
        }
    }
    
    
    


  • Hi @LeeK , i dont think thats the right way to do it, can you explain me what functionality are you trying to achieve,so that i can help you with an optimal solution.

    And by the way did you have a look into the documentation for Q_PROPERTY?


  • Moderators

    hi @LeeK said in How to Invoking QML Methods with c++ and connect(..,signal,...,slot) ?:

    I think your issue is this:

    void QmltoCppControl::nextScreen()
    {
    std::cout << "void QmltoCppControl::nextScreen() :: "<< std::endl;
    QVariantList varData;
    varData.clear();
    varData << 1;
    emit SendMsgtoQml_ChangeScreen((QVariant)varData);
    }

    You simply cast (c-style cast) a QvariantList to Qvariant. That can't be a valid QVariant



  • @Shrinidhi-Upadhyaya
    Thank you.
    but I already know Q_Property, it isn't what I want.


    @J.Hilk
    Thank you.
    I think it isn't the cast problem.
    But I could be wrong, so I tried what you said.
    It still doesn't work.
    And if it's a problem, the below code should work.
    But that doesn't work either.

    main.cpp

     QObject::connect(&qmltocppcontrol,SIGNAL(SendMsgtoQml_ChangeScreen()), qmlRootObject,SLOT(onChangeScreenQml()),Qt::QueuedConnection);
    

    QmltoCppControl.h

    void SendMsgtoQml_ChangeScreen();
    

    QmltoCppControl.cpp

    emit SendMsgtoQml_ChangeScreen();
    

    qml

    function onChangeScreenQml() {
            console.log("onChangeScreenQml\n");
            qmltocpp_id.controlEvent(0);    //0 = EVENT_APP_EXIT
        }
    


  • Hi @LeeK , you can do one thing, you can connect the "SendMsgtoQml_ChangeScreen()" signal to a slot and in that slot you can call your QML function which is in your case "onChangeScreenQml"

    For example:-

    //This is just dummy connect statement

    timer = new QTimer(this);
    
    connect(timer, SIGNAL(timeout()),
            this, SLOT(dummySlot()));
    
    timer->start(10000);
    

    //This is the slot where you can access your QML Function and object is the one which is pointing to your main.qml which in your case is "qmlRootObject" and "qmlHelloWorld" is a function in main.qml

    void DummyClass::dummySlot() {
    
    qDebug() << "Another Slot";
    
    QMetaObject::invokeMethod(object, "qmlHelloWorld");
    }
    

    You cant directly access any qml function like you have done in you main.cpp,you need to use QMetaObject::invokeMethod(). I dont know whether you can directly connect the signal to the QMetaObject::invokeMethod()!



  • @LeeK I think there a almost more easier way to do what you want!

    in main.cpp

    ...
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQuickView view;
        QmltoCppControl qmltocppcontrol;
        qmlRegisterType<QmltoCppControl>("QmltoCppControl", 1, 0, "QmltoCppControl");
        view.setSource(QUrl("qrc:/main.qml"));
        QObject *qmlRootObject = (QObject*)view.rootObject();
        view.rootContext()->setContextProperty("QmltoCppControl", &qmltocppcontrol);
        view.show();
    
        return app.exec();
    }
    

    For easier code comprension; in QmltoCppControl rename signal void SendMsgtoQml_ChangeScreen(QVariant varData) to void changeScreenRequest(QVariant varData)

    main.qml

    import QtQuick 2.9
    import QmltoCppControl 1.0
    
    Rectangle {
        id: main_rect_id
        x: 0
        y: 0
        width: 1920
        height: 560
        visible: true
    
        property int nScreenId: 0
    
       Connections {
            target: qmltocppcontrol
           onChangeScreenRequest: {
                console.log("onChangeScreenQml:" + varData);
                qmltocpp_id.controlEvent(0);    //0 = EVENT_APP_EXIT
                }
        }
    
        QmltoCppControl {
            id: qmltocpp_id
        }
    
        Text {
            id: textid;
            font.pixelSize: 30
            horizontalAlignment: Text.AlignHCenter
            text: qsTr("Hello World")
            anchors.centerIn: parent
        }
    
        Item {
            id: next_item_id
            x: main_rect_id.width/4
            y: 460
            width: main_rect_id.width/4
            height: 100
    
            Text {
                id: next_text_id
                font.pixelSize: 30
                horizontalAlignment: Text.AlignHCenter
                text: qsTr("NEXT")
                anchors.centerIn: parent
            }
    
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    qmltocpp_id.controlEvent(10);    //10 = EVENT_SCREEN_NEXT
                }
            }
    
        }
        Item {
            id: exit_item_id
            x: main_rect_id.width/4*3
            y: 460
            width: main_rect_id.width/4
            height: 100
    
            Text {
                id: exit_text_id
                font.pixelSize: 30
                horizontalAlignment: Text.AlignHCenter
                text: qsTr("EXIT")
                anchors.centerIn: parent
            }
    
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    qmltocpp_id.controlEvent(0);    //0 = EVENT_APP_EXIT
                }
            }
    
        }
    }```


  • @Shrinidhi-Upadhyaya
    Thank you.
    but, doesn't work .. T.T
    I tried the way you taught me.
    If "invokeMethod" is called directly from main as below, it works normally.

    main.cpp

    #include <QGuiApplication>
    #include <QQuickView>
    #include <QObject>
    #include <QtQml>
    #include <QQmlContext>
    #include "QmltoCppControl.h"
    
    int main(int argc, char *argv[])
    {
    #if defined(Q_OS_WIN)
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    #endif
    
        QGuiApplication app(argc, argv);
        QQuickView view;
        QmltoCppControl qmltocppcontrol;
        qmlRegisterType<QmltoCppControl>("QmltoCppControl", 1, 0, "QmltoCppControl");
        view.setSource(QUrl("qrc:/main.qml"));
        QObject *qmlRootObject = (QObject*)view.rootObject();
        view.rootContext()->setContextProperty("QmltoCppControl", &qmltocppcontrol);
        view.show();
    
        QVariant returnedValue;
        QVariant msg = "Hello from C++";
        QMetaObject::invokeMethod(qmlRootObject, "onChangeScreenQml", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, msg));
    
        return app.exec();
    }
    

    However, it still doesn't work with signal and slot.
    maybe, this is the QObject problem, but I don't know well... how to use it.
    Could you help me?

    QObject *qmlRootObject = (QObject*)m_obj->rootObject();
        QMetaObject::invokeMethod(qmlRootObject, "onChangeScreenQml", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, msg));
    

    this is the problem code... the way you taught me.
    main.cpp

    #include <QGuiApplication>
    #include <QQuickView>
    #include <QObject>
    #include <QtQml>
    #include <QQmlContext>
    #include "QmltoCppControl.h"
    
    int main(int argc, char *argv[])
    {
    #if defined(Q_OS_WIN)
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    #endif
    
        QGuiApplication app(argc, argv);
        QQuickView view;
        QmltoCppControl qmltocppcontrol;
        qmlRegisterType<QmltoCppControl>("QmltoCppControl", 1, 0, "QmltoCppControl");
        view.setSource(QUrl("qrc:/main.qml"));
        QObject *qmlRootObject = (QObject*)view.rootObject();
        view.rootContext()->setContextProperty("QmltoCppControl", &qmltocppcontrol);
        view.show();
    
        qmltocppcontrol.m_obj = &view;
    
        return app.exec();
    }
    

    QmltoCppControl.h

    #ifndef QMLTOCPPCONTROL_H
    #define QMLTOCPPCONTROL_H
    
    #include <QObject>
    #include <iostream>
    #include <QVariant>
    #include <QQuickView>
    #include <QDebug>
    
    //#define QARG(object,returnedValue, msg) QMetaObject::invokeMethod(object, "myQmlFunction", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, msg))
    
    class QmltoCppControl: public QObject
    {
        Q_OBJECT
    public:
        explicit QmltoCppControl(QObject *parent = 0);
    
        void init();
        QQuickView *m_obj;
    public:
        Q_INVOKABLE void controlEvent(int id);
    signals:
        void SendMsgtoQml_ChangeScreen();
    
    
    public slots:
        void Slot_ChangeScreen();
    
    private:
        void exitApp();
        void nextScreen();
    
        int testnum;
    };
    
    #endif // QMLTOCPPCONTROL_H
    

    QmltoCppControl.cpp

    ...
    QmltoCppControl::QmltoCppControl(QObject *parent)
        :QObject(parent)
    {
        connect(this, SIGNAL(SendMsgtoQml_ChangeScreen()), this, SLOT(Slot_ChangeScreen()));
    }
    ...
    void QmltoCppControl::nextScreen()
    {
        std::cout << "void QmltoCppControl::nextScreen() :: "<< std::endl;
        emit SendMsgtoQml_ChangeScreen();
    
    }
    
    void QmltoCppControl::Slot_ChangeScreen()
    {
        qDebug() << "Slot_ChangeScreen()" ;
        QVariant returnedValue;
        QVariant msg = "Hello from C++";
        QObject *qmlRootObject = (QObject*)m_obj->rootObject();
        QMetaObject::invokeMethod(qmlRootObject, "onChangeScreenQml", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, msg));
    }
    
    

    @KroMignon
    Thank you.
    I tried the way you taught me.
    and i get the below error.
    what is the problem???

    qrc:/main.qml:20:5: QML Connections: Cannot assign to non-existent property "onChangeScreenRequest"
    qrc:/main.qml:21: ReferenceError: qmltocppcontrol is not defined
    qrc:/main.qml:21: ReferenceError: qmltocppcontrol is not defined

    line 20 and 21 is...
    line20 ---> target: qmltocppcontrol
    line21 ---> onChangeScreenRequest: {

    and current, i try to the google search continually...

    ~~wow... ~~
    I am solving it a little bit. However, another problem came out..................omg...

    qrc:/main.qml:28: ReferenceError: varData is not defined

    Connections {
            target: qmltocpp_id
            onChangeScreenRequest: {
                console.log("onChangeScreenQml:" + varData);
                qmltocpp_id.controlEvent(0);    //0 = EVENT_APP_EXIT
            }
        }
    

    varData is parameter about signal ...
    i want to use the below parameter of code..

    void QmltoCppControl::nextScreen()
    {
        std::cout << "void QmltoCppControl::nextScreen() :: "<< std::endl;
        QVariantList varData;
        varData.clear();
        varData << "test";
        emit changeScreenRequest((QVariant)varData);
    
    }
    

    wow.... solved it!!!
    Thanks @KroMignon !! And thanks to everyone who helped!!!!



  • @LeeK You are very confusing in your comments, I don't really understand your problem...

    What do you want to do???

    As I have understand, you want to connect a object created from C++ with a QML code. Is this correct?
    For me:

    • the c++ object was qmltocppcontrol created in main.cpp
    • the QML instance was qmltocpp_id created in main.qml

    If this is not what you want to do, please be clear in what is to task you want to do!

    regards


  • Moderators

    //main.cpp
    #include <QApplication>
    #include <QQmlApplicationEngine>
    
    #include "myclass.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        myClass mClass;
        QObject *rootElement = engine.rootObjects().first();
    
        QObject::connect(&mClass,SIGNAL(mySignal(QVariant)), rootElement, SLOT(qmlFunction(QVariant)));
    
        return app.exec();
    }
    
    
    //myClass.h
    #ifndef MYCLASS_H
    #define MYCLASS_H
    
    #include <QObject>
    #include <QVariant>
    #include <QTimer>
    #include <QTime>
    
    class myClass : public QObject
    {
        Q_OBJECT
    public:
        explicit myClass(QObject *parent = nullptr) :QObject(parent)
        {
            QTimer *t = new QTimer(this);
    
            connect(t, &QTimer::timeout, this, [=]()->void{
                emit mySignal(QVariant::fromValue(QTime::currentTime().toString("hh.mm.ss")));
            });
    
            t->start(1000);
        }
    
    signals:
        void mySignal(QVariant msg);
    
    public slots:
    };
    
    #endif // MYCLASS_H
    
    
    //main.qml
    import QtQuick 2.12
    import QtQuick.Window 2.2
    
    Window
    {
        id:rootWindow
        width: 400
        height: 250
    
        visible:  true
    
        function qmlFunction(msg) {
            console.log(msg)
            myText.text = msg;
        }
    
        Text {
            id:myText
            anchors.centerIn: parent
            text: qsTr("text")
        }
    }
    
    


  • Hi @LeeK , i dont know how you tried it, there is no way in which it will not work, i guess you must have missed passing a paramter or some silly mistakes. And the code which you have posted is completely different from what you had asked, you actually wanted to connect a signal to a function in qml and when i asked you have seen the Q_PROPERTY documentation, you told me yes you have seen, if you had seen Q_PROPERTY documentation you would even stumbled upon Connections documentation, please be clear on what is your requirement.



  • @KroMignon
    Thank you.

    I am sorry for the confusion in my comments.
    I solved the problem in the way that you suggested.
    Thank you.

    @J-Hilk
    Thank you.
    I solved the problem. but, additionally i will try to the way that you suggested.

    @Shrinidhi-Upadhyaya
    Thank you.
    I don't know what you mean.
    I did not know the part of the mistake or the wrong parameter you mentioned. So I asked the question.
    I have already solved this problem with the help of others.


Log in to reply