Problem connecting signal/slot between 2 new QML type instances



  • Hi

    I am struggling a bit with a signal/slot connection between 2 new QML type instances. I am not sure I am doing things properly and I have been through the net for 2 days without finding proper solution. You may be able to help. I am working with Qt 5.8.

    I have created 2 new QML types, let's say ObjectA and ObjectB. For now, neither of them is used to display anything so they only inherit from QObject.
    I have created a basic application window, in QML, including only one button. I want the button to call a method from the ObjectA-related class (Q_INVOKABLE). Then I want the method to emit a signal connected to a slot declared by the ObjectB-related class. Ultimately, I want the ObjectB-related class to inherit from QQuickPaintedItem, so that the signal emission generates an visual update in the GUI.
    The connection between the signal and the slot is done in the main, which seems to be the problem, even if I saw some examples doing so on the net.

    Here is my code:

    ObjectA.qml (replace "A" by "B" to get ObjectB.qml)

    import ObjA 1.0
    import QtQuick 2.1
    
    ObjectA
    {
    }
    

    main.qml

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.0
    import ObjA 1.0
    import ObjB 1.0
    
    ApplicationWindow {
      visible: true
      width: 640
      height: 480
      title: qsTr("Hello World")
    
      Button {
          id: button
          x: 270
          y: 206
          text: qsTr("Activate")
    
          onClicked: {
            oA.activateBFromA()
          }
      }
    
      ObjectA {
          id: oA
      }
    
      ObjectB {
          id: oB
      }
    }
    

    ObjectA.h

    #ifndef OBJECTA_H
    #define OBJECTA_H
    
    #include <QObject>
    #include <QDebug>
    
    class objectA : public QObject
    {
      Q_OBJECT
    
    public:
      objectA(QObject *parent = 0);
      ~objectA();
    
      Q_INVOKABLE void activateBFromA();
    
    signals:
      void bActivated (const int& value);
    };
    
    #endif
    

    ObjectA.cpp

    #include "ObjectA.h"
    #include <QDebug>
    
    objectA::objectA(QObject *parent)
      : QObject(parent)
    {
    }
    
    objectA::~objectA()
    {
    }
    
    void objectA::activateBFromA()
    {
      qDebug() << "Emit from A";
      emit this->bActivated(5);
    }
    

    ObjectB.h

    #ifndef OBJECTB_H
    #define OBJECTB_H
    
    #include <QObject>
    #include <QDebug>
    
    class objectB : public QObject
    {
      Q_OBJECT
    
    public:
      objectB(QObject *parent = 0);
      ~objectB();
    
    public slots:
      void onBActivated (const int& value)
      {
        for (int i = 0; i < value; i++)
          qDebug() << "Activation (from B) !";
      }
    };
    
    #endif
    

    ObjectB.cpp

    #include "ObjectB.h"
    #include <QDebug>
    
    objectB::objectB(QObject *parent)
      : QObject(parent)
    {
    }
    
    objectB::~objectB()
    {
    }
    

    and finally main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlComponent>
    
    #include "ObjectA.h"
    #include "ObjectB.h"
    
    int main(int argc, char *argv[])
    {
      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
      QGuiApplication app(argc, argv);
    
      QQmlApplicationEngine engine;
    
      qmlRegisterType<objectA>("ObjA", 1,0, "ObjectA");
      qmlRegisterType<objectB>("ObjB", 1,0, "ObjectB");
    
      QQmlComponent componentA(&engine, QUrl("qrc:ObjectA.qml"));
      objectA* oA = qobject_cast<objectA*>(componentA.create());
    
      QQmlComponent componentB(&engine, QUrl("qrc:ObjectB.qml"));
      objectB* oB = qobject_cast<objectB*>(componentB.create());
    
      QObject::connect(oA, &objectA::bActivated, oB, &objectB::onBActivated);
    
      engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    
      return app.exec();
    }
    

    My console only displays: "Emit from A", it seems that the connection is not working.
    Any help appreciated, thanks a lot in advance.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    The objects you are connecting in your main function are not the same as the one your create in your main.qml file.

    You need to first load main.qml, then search for the objects and only after that connect them.


  • Moderators

    Hi! Either do it like @SGaist suggested, or connect the objects inside your QML file:

    myobjecta.h

    #ifndef MYOBJECTA_H
    #define MYOBJECTA_H
    
    #include <QObject>
    
    class MyObjectA : public QObject
    {
        Q_OBJECT
    public:
        explicit MyObjectA(QObject *parent = nullptr);
        Q_INVOKABLE void doIt() { emit doItCalled(); }
    
    signals:
        void doItCalled();
    };
    
    #endif // MYOBJECTA_H
    

    myobjectb.h

    #ifndef MYOBJECTB_H
    #define MYOBJECTB_H
    
    #include <QObject>
    #include <QDebug>
    
    class MyObjectB : public QObject
    {
        Q_OBJECT
    public:
        explicit MyObjectB(QObject *parent = nullptr);
    
    public slots:
        void doItSlot() {
            qDebug() << "hello";
        }
    };
    
    #endif // MYOBJECTB_H
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlComponent>
    
    #include <myobjecta.h>
    #include <myobjectb.h>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<MyObjectA>("io.qt.forum", 1, 0, "MyObjectA");
        qmlRegisterType<MyObjectB>("io.qt.forum", 1, 0, "MyObjectB");
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    
        return app.exec();
    }
    

    main.qml

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.0
    
    import io.qt.forum 1.0
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
    
        MyObjectA {
            id: objA
            onDoItCalled: objB.doItSlot() 
        }
    
        MyObjectB {
            id: objB
        }
    
        Button {
            anchors.centerIn: parent
            text: "Click me"
            onClicked: objA.doIt()
        }
    }
    

  • Moderators

    Oh, BTW, the forum thread Calling JS functions in loaded components from C++ contains an example for how to find QML-objects from the C++ side.



  • 1000 thanks @SGaist and @Wieland !!! I tried both solutions everything works.

    For the sake of others, here is then the modifications in my example if I am adopting the first solution:

    ObjectA.qml (replace "A" by "B" to get ObjectB.qml)

    import ObjA 1.0
    import QtQuick 2.1
    
    ObjectA
    {
        objectName: "objA_id"    // do not forget the quotes...
    }
    

    ObjectB.h

    ...
    public slots:
      void activateB (const int& value)
      {
        for (int i = 0; i < value; i++)
          qDebug() << "Activation (from B) !";
      }
    ...
    

    main.cpp

    int main(int argc, char *argv[])
    {
      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
      QGuiApplication app(argc, argv);
    
      qmlRegisterType<objectA>("ObjA", 1,0, "ObjectA");
      qmlRegisterType<objectB>("ObjB", 1,0, "ObjectB");
    
      QQmlApplicationEngine engine;
      engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    
      objectA* oA = qobject_cast<objectA*>(engine.rootObjects().first()->findChild<QObject*>("objA_id"));
      objectB* oB = qobject_cast<objectB*>(engine.rootObjects().first()->findChild<QObject*>("objB_id"));
    
      QObject::connect(oA, &objectA::bActivated, oB, &objectB::activateB);
    
      return app.exec();
    }
    

    Thanks again guys. That was great help.


Log in to reply
 

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