Solved 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. -
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.
-
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() } }
-
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.