Solved Connect QML Signal with C++ Slot
-
Hey,
i'm new to QML and want to connect a signal from QML to my C++ class. I have read the tutorials but it doesn't work :(
Here are my files:main.qml
ApplicationWindow { id: window visible: true width : 640 height: 480 title: qsTr("Test") header: ToolBar { Material.foreground: "white" RowLayout { spacing: 20 anchors.fill: parent ToolButton { contentItem: Image { fillMode: Image.Pad horizontalAlignment: Image.AlignHCenter verticalAlignment: Image.AlignVCenter source: "qrc:/images/drawer.png" } onClicked: drawer.open() } Label { id: titleLabel text: "Test" font.pixelSize: 20 elide: Label.ElideRight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter Layout.fillWidth: true } } } Drawer { id: drawer objectName: "drawer" width: Math.min(window.width, window.height) / 4 height: window.height ListView { id: listView currentIndex: -1 anchors.fill: parent delegate: ItemDelegate { width: parent.width height: 100 Item { width: parent.width height: parent.height RowLayout { id: layout spacing: 20 anchors.fill: parent Image { Layout.preferredHeight: 50 Layout.preferredWidth: 50 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter source: model.imagePath } Text { text: model.title } } } highlighted: ListView.isCurrentItem onClicked: { if (listView.currentIndex != index) { listView.currentIndex = index titleLabel.text = model.title stackView.replace(model.source) } drawer.close() } } model: ListModel { ListElement{title: "Home"; source: "qrc:/Home.qml"; imagePath: "qrc:/images/home.png"; } } ScrollIndicator.vertical: ScrollIndicator { } } } StackView { id: stackView objectName: "stackView" anchors.fill: parent } }
Home.qml
import QtQuick 2.6 import QtQuick.Controls 2.0 Pane { id: homePane signal qmlSignal() Column { anchors.fill: parent ToolButton { id: myButton anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter text: "Click Me" onClicked: { console.log("clicked") homePane.qmlSignal() } } } }
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQuickStyle> #include <QDebug> class MyClass : public QObject { Q_OBJECT public: MyClass(QObject *parent = 0) : QObject(parent) {} ~MyClass() {} public slots: void clicked() { qDebug() << "Slot"; } }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQuickStyle::setStyle("Default"); QQmlApplicationEngine engine; engine.load(QUrl(QLatin1String("qrc:/main.qml"))); QQmlApplicationEngine engine2; engine2.load(QUrl(QLatin1String("qrc:/Home.qml"))); QObject *homePane = engine2.rootObjects().first(); MyClass *slot = new MyClass(); qDebug() << QObject::connect(homePane, SIGNAL(qmlSignal()), slot, SLOT(clicked())); return app.exec(); }
The QObject::connect function returns true.
But when i click my button the slot will not be invoked!
Thanks for help!
-
@beecksche That is because the button you click is under
QQmlApplicationEngine engine
context while you have connected to a signal from a QML file under context ofQQmlApplicationEngine engine2
.
There is no need for the second one. -
@p3c0 Thanks for the quick reply.
But how can i acces the homePane from the engine? -
Hi! Here is a small example how to do it similar to how you already tried it. I will also write another posting with what I think is better practice.
stuff.h
#ifndef STUFF_H #define STUFF_H #include <QObject> class Stuff : public QObject { Q_OBJECT public: explicit Stuff(QObject *parent = 0); public slots: void onClicked(); }; #endif // STUFF_H
stuff.cpp
#include "stuff.h" #include <QDebug> Stuff::Stuff(QObject *parent) : QObject(parent) { } void Stuff::onClicked() { qDebug() << "clicked"; }
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "stuff.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); Stuff stuff; QQmlApplicationEngine engine; engine.load(QUrl(QLatin1String("qrc:/main.qml"))); QObject* home = engine.rootObjects().first()->findChild<QObject*>("MyHomeObject"); QObject::connect(home, SIGNAL(clicked()), &stuff, SLOT(onClicked())); return app.exec(); }
main.qml
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") Home { objectName: "MyHomeObject" } }
Home.qml
import QtQuick 2.7 import QtQuick.Controls 2.0 Rectangle { id: rect width: 100 height: 100 color: "orange" signal clicked() Button { anchors.centerIn: parent text: "Click me" onClicked: rect.clicked() } }
-
@beecksche said in Connect QML Signal with C++ Slot:
@p3c0 Thanks for the quick reply.
But how can i acces the homePane from the engine?In the similar manner you need to get access to
Home
object. -
So, now what I think is better: You create one single object ("backend") that acts as the C++ interface to your business logic. The QtQuick GUI will only interact with the C++ part through that object.
backend.h
#ifndef BACKEND_H #define BACKEND_H #include <QObject> class Backend : public QObject { Q_OBJECT public: explicit Backend(QObject *parent = 0); public slots: void doSomething(); }; #endif // BACKEND_H
backend.cpp
#include "backend.h" #include <QDebug> Backend::Backend(QObject *parent) : QObject(parent) { } void Backend::doSomething() { qDebug() << "Do something"; }
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "backend.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; Backend backend; engine.rootContext()->setContextProperty("backend", &backend); 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 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") Home {} }
Home.qml
import QtQuick 2.7 import QtQuick.Controls 2.0 Rectangle { width: 100 height: 100 Button { anchors.centerIn: parent text: "Click me" onClicked: backend.doSomething() } }
-
Wow, thanks a lot for the answers! That's amazing!
I'll try the solutions as soon as possible!Edit1:
@Wieland The backend mechanism is really nice! Is there a way to connect the signals from the backend to qml functions?Edit2:
Sometimes i should first search by myself ... I found the the answer:
http://doc.qt.io/qt-5/qtqml-cppintegration-contextproperties.htmlThanks for the help !
-
@beecksche Great that you already solved it :-) But if you should encounter any further problems then don't hesitate to just ask again.
-