How to connect a signal from C++ to a QML file in a QQuickWidget?



  • Hello,

    this is my first try to use something with QML.

    I have a C++ Qt GUI application with a normal MainWindow.
    There I have a QuickWidget which loads a QML file with the following code:

    import QtQuick 2.3
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Extras 1.4
    
    Rectangle
    {
        color: "#201d1d"
        width: 600; height: 200
    
        function setvalue(value){ m1.value = value; }
    
        CircularGauge {
    
            transform: Rotation { origin.x: 0; origin.y: 0; angle: -90}
    
            id:m1
            width: 150
            height: 150
            x: 50
            y: 150
    
            style: CircularGaugeStyle {
                    minimumValueAngle: 0
                    maximumValueAngle: -120
            }
    
            minimumValue: 0
            maximumValue: 120
    }
    }
    

    The QML file is directly loaded using the QtCreator QQuickWidget properties.

    Now I would like to connect a function like the following to the CircularGauge in the QML file to set a new gauge value:

    void newValue(const double &value);
    connect(this, SIGNAL(newValue(double)), ui->QuickWidgetName, SLOT(setValue(double)));
    

    I googled the last hours, but I found no solution which worked :-(

    It would be really nice if you could help me with that.
    Thanks :-)



  • Example using the QQmlApplicationEngine :

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
    QObject *rootObject = qobject_cast<QObject*>(engine.rootObjects().first());
    
    QObject::connect(this, &MyClass::mySignal, [=](double value) {
        QMetaObject::invokeMethod(rootObject, "setValue", Q_ARG(double, value));
    });
    

    It's not tested but it should work. You can call QML functions using the invokeMethod function. rootObject is the QObject containing the function - in your case it is the rootObject(1st top-level object in qml). The connect is using the new sexy lambda expressions to call invokeMethod.



  • Thank you very much :-)

    I tried it, but it is not working.
    I used the following code with the QML file from above:

        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/qml/QtQuickProgressBar.qml")));
    
        QObject *rootObject = qobject_cast<QObject*>(engine.rootObjects().first());
    
        QObject::connect(this, &MainWindow::setGauge, [=](double value) {
            QMetaObject::invokeMethod(rootObject, "setValue", Q_ARG(double, value));
        });
    
        emit setGauge(10.5);
    

    After the signal is fired I get the following error:
    QMetaObject::invokeMethod: No such method QtQuickProgressBar_QMLTYPE_41::setValue(double)
    What is wrong with the function function setvalue(value){ m1.value = value; }?

    As I said I am completely new to QML but as I understood you do not name a datatype in a function, right?

    Furthermore I have one big question-mark.
    I load my QML file in the GUI to a QQuickWidget and then I load it a second time into the QQmlApplicationEngine and then if the slot in the QML file is triggered in the Engine it is even visible in the GUI because the QML file is loaded twice?


  • Moderators

    @onek24 said:

    It's not tested but it should work. You can call QML functions using the invokeMethod function. rootObject is the QObject containing the function - in your case it is the rootObject(1st top-level object in qml). The connect is using the new sexy lambda expressions to call invokeMethod.

    @RolBri said:

    QMetaObject::invokeMethod: No such method QtQuickProgressBar_QMLTYPE_41::setValue(double)
    What is wrong with the function function setvalue(value){ m1.value = value; }?

    Be aware of 2 things (both explained in http://doc.qt.io/qt-5/signalsandslots-syntaxes.html#connecting-c-objects-to-qml-objects ):

    1. To connect C++ and QML, you need to use the old string-based connection. The new sexy lambda expression won't work.
    2. C++ sees the parameter type as QVariant, not double.


  • Thanks,

    I changed it to QVariant and the "old" connection format.
    Now I do not get any errors, but I can not see anything changing in the GUI.

    I guess it is not enough to load the same QML file to the QQmlApplicationEngine and the QQuickWidget.
    So how can I tell my QQuickWidget to show the object I created with the QQmlApplicationEngine?


  • Moderators

    @RolBri said:

    I guess it is not enough to load the same QML file to the QQmlApplicationEngine and the QQuickWidget.

    Correct. This will give you 2 separate QML objects. It's a bit like creating 2 separate MainWindows.

    So how can I tell my QQuickWidget to show the object I created with the QQmlApplicationEngine?

    You can't. QQuickWidget uses its own internal engine.

    Connect to the QQuickWidget's root object instead (see the link I gave you), and don't create a second QQmlEngine.



  • @JKSH said:

    Be aware of 2 things (both explained in http://doc.qt.io/qt-5/signalsandslots-syntaxes.html#connecting-c-objects-to-qml-objects ):

    1. To connect C++ and QML, you need to use the old string-based connection. The new sexy lambda expression won't work.
    2. C++ sees the parameter type as QVariant, not double.

    Huh that's strange. It definitely works in my cases using the new sexy lambda expression.

    Also from the QMetaObject::invokeMethod docs i took the following example:

    QMetaObject::invokeMethod(obj, "compute", Qt::DirectConnection,
                              Q_RETURN_ARG(QString, retVal),
                              Q_ARG(QString, "sqrt"),
                              Q_ARG(int, 42),
                              Q_ARG(double, 9.7));
    

    I was using QVariant but after i read this code-snippet in the docs i changed it in my example code which i wrote some posts ealier.


  • Moderators

    @onek24 said:

    Huh that's strange. It definitely works in my cases using the new sexy lambda expression.

    Argh, my bad... I got mixed up between your lambda and the QML function. You're right, the lambda is fine, because you're not connecting directly to QML.

    Also from the QMetaObject::invokeMethod docs i took the following example:

    QMetaObject::invokeMethod(obj, "compute", Qt::DirectConnection,
                              Q_RETURN_ARG(QString, retVal),
                              Q_ARG(QString, "sqrt"),
                              Q_ARG(int, 42),
                              Q_ARG(double, 9.7));
    

    That works if the object has a C++ function with the signature like this: QString compute (const QString& value1, int value2, double value3)

    However, all QML functions are JavaScript functions, and JavaScript functions take var parameters. var in JavaScript/QML maps to QVariant in C++.

    • QML declaration: function setvalue(value)
    • C++ equivalent: void setValue(const QVariant& value)

    So, you must use QVariant in the Q_ARG() macro.



  • Thank you very much, now it works fine :-)

    Here is the solution:

        ui->gauge_quick->setSource(QUrl(QStringLiteral("qrc:/qml/QtQuickProgressBar.qml")));
        ui->gauge_quick->show();
        auto rootObject = ui->gauge_quick->rootObject();
    
        connect(this, SIGNAL(setGauge(QVariant)), rootObject, SLOT(setvalue(QVariant)));
    
        emit setGauge(15.5);
    

Log in to reply
 

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