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!


  • Moderators

    @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 of QQmlApplicationEngine 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?


  • Moderators

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

  • Moderators

    @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.


  • Moderators

    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.html

    Thanks for the help !


  • Moderators

    @beecksche Great that you already solved it :-) But if you should encounter any further problems then don't hesitate to just ask again.


Log in to reply
 

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