C++ signal to QML slot in Qt
Asked Answered
D

6

69

I want to send a Signal from C++ to a Slot in my QML File. I already got it working without and primitive type parameters, although if I want to send a QString to my QML Slot I get an error whilst connecting.

I connect in main.cpp

QObject *contentView = rootObject->findChild<QObject*>(QString("contentView"));
QObject::connect(&myObj,      SIGNAL(finishedGatheringDataForItem(QString)), 
                 contentView, SLOT(updateViewWithItem(QString)));

the relavant part of my qml File

Rectangle {
        objectName: "contentView"
        function updateViewWithItem(string) { console.log('got some Items'); }  // slot
}

Error:

Object::connect: No such slot QDeclarativeRectangle_QML_2::updateViewWithItem(QString)
Dalliance answered 12/1, 2012 at 11:14 Comment(1)
thanks, this Q/A really helped me. for future reference, a great example is now up here: github.com/andrewrjones/qml2-to-cpp-and-back-signalsCounterreply
C
37

I think it would be best if you check this tutorial:

http://doc.qt.io/qt-4.8/qtbinding.html

especially this section:

http://doc.qt.io/qt-4.8/qtbinding.html#receiving-signals

I think your mistake in this case might either be that you didn't declare it as a slot or you didn't make it invocable. Both options are explained in the Qt Tutorial.

Also, you need to use a QVariant in order to exchange data between C++ and QML. You can also register types, e.g. Widgets and stuff, so that you can use them in QML as a "native" type like a rectangle. In most cases this is not recommended, except if you need some certain extern class or some data that you cannot display otherwise in your QML Interface.

The reason for the QVariant is the Script based approach of QML. The QVariant basically contains your data and a desription of the data type, so that the QML knows how to handle it properly. That's why you have to specify the parameter in QML with String, int etc.. But the original data exchange with C++ remains a QVariant

I have used the qmlRegisterType before, but it is a very inconvenient Solution for simple data types. It is rather used for more complex data, such as custom Widgets, Canvas or Video elements that QML does not natively support or extended QStandardItemModels . It is a more convenient way to exchange data between QML and C++ and does not need Signals or Slots in first instance, because the QStandardItemModel updates the GUI automatically. For using the QStandardItemModel you need to register the Type with qmlRegisterType.. . The Model can then be used in Model based Views such as the ListView etc.

I attached a tutorial for this topic, it describes how to use the QListModel.

http://doc.qt.io/qt-4.8/qdeclarativemodels.html

Cannonade answered 12/1, 2012 at 19:16 Comment(2)
I do not use a QDeclarativeView subclass here. As I pointed out in my question, it all works if I just leave out the parameter. Do I have to introduce QString to my QML in some way?Dalliance
In that case, try using a QVariant. I had this problem a couple of times that QML does not recognize other data types than QVariant.Cannonade
T
49

You should use Connections in this case (maybe it's the only way to connect).

  1. Put your object myObj to QML file by setContextProperty

    qmlVectorForm->rootContext()->setContextProperty("YourObject", myOb);
    
  2. Your signal is

    finishedGatheringDataForItem(QString signalString)
    
  3. In QML file, add Connectios likes below:

    Connections {
        target: YourObject 
        onFinishedGatheringDataForItem: {
            qmlString = signalString
        }
    }
    
Tool answered 23/3, 2015 at 2:30 Comment(5)
Where in the QML file?Quincey
This might cause delays in the UI when data is exchanged either at high frequency or in large quantities.Cannonade
Sure that SIGNAL SLOT not always has good performance. So for optimizing, you should process data before sending it through SIGNAL. In some case if you connect between two threads, plz notice that do not send SIGNAL with a so high rate, it will cause UI stuck. You should make a mechanism to control the frequency of sending (maybe use timer or delay)Tool
@Quincey Put the Connections object within the QML component that should receive the connection. Often it will be the root component. See the examples in doc.qt.io/qt-5/qml-qtqml-connections.htmlChristean
I still can't get use to the major difference between the QML way of doing this thing and the good old C++ code. Basically the slot is generated internally without the need to define it manually (since in QML the slots are basically a side-feature of the signal decleration).Shaunda
C
37

I think it would be best if you check this tutorial:

http://doc.qt.io/qt-4.8/qtbinding.html

especially this section:

http://doc.qt.io/qt-4.8/qtbinding.html#receiving-signals

I think your mistake in this case might either be that you didn't declare it as a slot or you didn't make it invocable. Both options are explained in the Qt Tutorial.

Also, you need to use a QVariant in order to exchange data between C++ and QML. You can also register types, e.g. Widgets and stuff, so that you can use them in QML as a "native" type like a rectangle. In most cases this is not recommended, except if you need some certain extern class or some data that you cannot display otherwise in your QML Interface.

The reason for the QVariant is the Script based approach of QML. The QVariant basically contains your data and a desription of the data type, so that the QML knows how to handle it properly. That's why you have to specify the parameter in QML with String, int etc.. But the original data exchange with C++ remains a QVariant

I have used the qmlRegisterType before, but it is a very inconvenient Solution for simple data types. It is rather used for more complex data, such as custom Widgets, Canvas or Video elements that QML does not natively support or extended QStandardItemModels . It is a more convenient way to exchange data between QML and C++ and does not need Signals or Slots in first instance, because the QStandardItemModel updates the GUI automatically. For using the QStandardItemModel you need to register the Type with qmlRegisterType.. . The Model can then be used in Model based Views such as the ListView etc.

I attached a tutorial for this topic, it describes how to use the QListModel.

http://doc.qt.io/qt-4.8/qdeclarativemodels.html

Cannonade answered 12/1, 2012 at 19:16 Comment(2)
I do not use a QDeclarativeView subclass here. As I pointed out in my question, it all works if I just leave out the parameter. Do I have to introduce QString to my QML in some way?Dalliance
In that case, try using a QVariant. I had this problem a couple of times that QML does not recognize other data types than QVariant.Cannonade
I
2

Solution without Connections and any context is by connecting not signal-slot, but signal-signal. Found here. Example code is as follows.

qml:

Window{
    signal qmlSend(string textOut)
    signal qmlReceive(string textIn)
    onQmlReceive:{
      console.log(textIn)
    }
}

Header file of Background class contains

public signals:
    void cppSend(QString textOut);
public slots:
    void cppReceive(QString textIn);

And main.cpp connects them in this way:

1.From qml to cpp:

QObject::connect(qmlRootObject, SIGNAL(qmlSend(QString)),
                backgroundObject, SLOT(cppReceive(QString)));

2.From cpp to qml:

QObject::connect(backgroundObject, SIGNAL(cppSend(QString)),
                 qmlRootObject, SIGNAL(qmlReceive(QString)));
Immoderation answered 21/1, 2020 at 17:35 Comment(0)
I
2

For those who also stumbled upon this question, I want to say that Everything is much simpler. You just need the signal from C++ to have QVariant arguments. For example:

QObject::connect(&recipient, SIGNAL(resTalk(QVariant)), engine.rootObjects().at(0)->findChild<QObject*>("winSettings"),
                     SLOT(showWithErrorNetwork(QVariant)));

My signal is declared like this:

signals:
    void resTalk(QVariant res);

So I'm calling the signal:

emit resTalk(true); //For more complex types, use  'emit yourSignal(QVariant(yourArg))'

And here is the slot I have in QML:

    function showWithErrorNetwork(isNoError=false) {
        if(!isNoError) {
            visible = true
            warningText.text = "Network error. Check the data."
            warningText.visible = true
        }
    }
Ilan answered 10/2, 2022 at 6:33 Comment(0)
T
1

I have tried a lot of solutions to succeed in just update QML from a C++ signal but many did not work. This solution works and has been tested, it is based on this answer: https://mcmap.net/q/281892/-how-to-update-a-qml-text-with-c (by @Adriano Campos)

You can send data from C++ to qml using signals, like this:

main.cpp:

#include <QQmlContext>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    // Class init
    YourClass yourObject;

    // Embedding C++ Objects into QML with Context Properties
    QQmlContext* ctx = engine.rootContext();
    ctx->setContextProperty("yourObject", &yourObject);

    return app.exec();
}

main.qml:

import QtQuick 2.6

Window {
    id: mainWindow

    Connections {
        target: yourObject
        onSignalData: {
            console.log("Data: " + signal_param)
            textToChange.text = "Changed to: " + signal_param
        }
    }

    Text {
        id: textToChange
        text: "beforeChange"
    }
}

yourClass.h:

class YourClass : public QObject
{
Q_OBJECT
signals:
    // Signal from YourClass
    void signalData(QString signal_param);
}

yourClass.cpp:

emit signalData("Hello QML"); // Signal from yourClass

A complete tutorial about "How to Expose a Qt C++ Class with Signals and Slots to QML" is available on this page: https://felgo.com/cross-platform-development/how-to-expose-a-qt-cpp-class-with-signals-and-slots-to-qml

Trueblue answered 13/3, 2021 at 18:40 Comment(0)
N
0

Why not use rootContext?

in c++ side you have:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

//--------------------------------------------------------
#include <myClass.h>
//--------------------------------------------------------

int main(int argc, char *argv[])
{

    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    //--------------------------------------------------------
    myClass * myobj = new myClass(&app);
    //--------------------------------------------------------

    //--------------------------------------------------------
    engine.rootContext()->setContextProperty("myobj",myobj);
    //--------------------------------------------------------

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);
    return app.exec();
}

and in qml side you have:

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    //--------------------------------------------------------
    Component.onCompleted: {
       myobj.onSomeSignal.connect(signalHandling) 
    }
    //--------------------------------------------------------

    //--------------------------------------------------------
    function signalHandling(){
       console.log("Signal emitted from c++ side")
    }
    //--------------------------------------------------------
}

Navy answered 14/2, 2021 at 6:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.