This is a text-only version of the following page on https://raymii.org: --- Title : QT / QML Signals and Slots with C++ Author : Remy van Elst Date : 25-02-2021 URL : https://raymii.org/s/snippets/Cpp_QT_QML_Signals_and_Slots.html Format : Markdown/HTML --- QT has an Observer mechanism built in, which they call '[Signals and Slots](https://web.archive.org/web/20210210180603/https://doc.qt.io/qt-5/signalsandslots.html)'. It allows objects to communicate with each other without having to have knowledge of either ones internals. By inheriting from `QObject` and defining a few `Q_PROPERTY` macro's, the QT Meta Object Compiler (`moc`) does all the hard work for you. Inside a C++ class this all works handy dandy and is reasonable easy to follow, but when using QML it requires a bit more work. This small example shows you how to bind QML and C++ together using signals and slots, in QT 5.12.

Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below. It means the world to me if you show your appreciation and you'll help pay the server costs:

GitHub Sponsorship

PCBWay referral link (You get $5, I get $20 after you've placed an order)

Digital Ocea referral link ($200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!)

Below a screenshot of the application. It's nothing more than a simple counter that increments with a button or is set via a text input field, but it is enough to get you started. ![screenshot][1] Because this signal/slot construction is mostly string based, you cannot use the refactoring tools provided by your IDE. If your method is based on `value` and you want to change `value` to, lets say, `something`, you need to change the `Q_PROPERTY`, the QML usage and bindings and all the regular C++ code. Not very obvious if you're not familiar with how QT signals and slots and QML work. For fun I compiled this example application to Webassembly as well. You can [run it here][5], or at the bottom of this page it's embedded as an `iframe`. ### Summary Because this is a small snippet, it lacks the explanation and depth you normally get from my articles. Some code comments are provided, but the QT documentation is recommended reading in this case: - [Signals and slots][2] - [Interaction with QML and C++][4] It's explained there very extensive. That is also why I wrote up this summary, due to all the comprehensive documentation, it's hard to get started with something small. My example code has a C++ class named `Counter`, with one private `long long` named `m_Value`. In the QML file I want to use this class and its methods, including the QT Signal/Slot. The class must inherit from `QObject` and you must place the `Q_OBJECT` macro in the header: class Counter : public QObject { Q_OBJECT [...] The methods for setting and getting the value are as you'd expect: long long value() const { return m_Value; }; [...] void Counter::setValue(long long value) { if (value == m_Value) return; m_Value = value; emit valueChanged(value); } In the above method you see the `emit` keyword. That's a blank define, for clarity. The function `valueChanged()` is called. This is our `signal`, as in the header file: signals: void valueChanged(long long newValue); The `setValue()` method is our `slot`: public slots: void setValue(long long value); These are accessible to QML by this `Q_PROPERTY` line: Q_PROPERTY(long long value READ value WRITE setValue NOTIFY valueChanged) You could also connect these up to things via `QObject::connect()` but that is out of this snippets scope. That is for when you use the signaling inside C++. These lines in `main.cpp` is also required, it adds your class to QML so to say: QQmlApplicationEngine engine; Counter myCounter; QQmlContext *context = engine.rootContext(); context->setContextProperty("MyCounter", &myCounter); After this you can access `MyCounter` inside of QML as if it was a regular C++ class. FOr example, to call the `Counter::value()` method: Text { text: "Counter: " + MyCounter.value + "." } Or the `Counter::setValue()` method: Button { text: qsTr("Set counter to 10") // C++ method Counter::setValue(long long), bound via Q_PROPERTY onClicked: MyCounter.setValue(10) } Due to the magic of the `moc` and the extra code it generates via `Q_PROPERTY`, when you increment like in the below example, it knows which value to increment and has generated correct operator overloads for it: Button { text: qsTr("Increase Counter") onClicked: ++MyCounter.value } You can also receive the C++ signal right in QT. We've defined `valueChanged` as the signal and via a `Connection` with `onValueChanged` (capitals matter here, prefix your method with `on` and change the first character of your method name to a capital) you can do things in QML. Like below, where I have a local variable that is incremented each time the signal is received: Text { property int changeCount: 0 id: labelChanged text: "Count has changed " + changeCount + " times." // Receive the valueChanged NOTIFY Connections { target: MyCounter onValueChanged: { ++labelChanged.changeCount } } } For a bi-directional binding example, look at the last `TextInput` in QML. It shows the current value of the C++ class, updates when the value is updated and when you enter a number, it updates the C++ class. ### Example code Create a project folder and place all the files there under the filenames provided. The project is also available on [github, here][3]. #### qmlcppsignalexample.pro QT += quick CONFIG += c++11 SOURCES += \ counter.cpp \ main.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Additional import path used to resolve QML modules just for Qt Quick Designer QML_DESIGNER_IMPORT_PATH = # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target HEADERS += \ counter.h #### qml.qrc main.qml #### main.cpp #include #include #include #include #include "counter.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; Counter myCounter; QQmlContext *context = engine.rootContext(); /* Below line makes myCounter object and methods available in QML as "MyCounter" */ context->setContextProperty("MyCounter", &myCounter); 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(); } #### counter.h #ifndef COUNTER_H #define COUNTER_H #include class Counter : public QObject { Q_OBJECT Q_PROPERTY(long long value READ value WRITE setValue NOTIFY valueChanged) public: explicit Counter(QObject *parent = nullptr); long long value() const { return m_Value; }; public slots: void setValue(long long value); signals: void valueChanged(long long newValue); private: long long m_Value {0} ; }; #endif // COUNTER_H #### counter.cpp #include "counter.h" Counter::Counter(QObject* parent) : QObject(parent) { } void Counter::setValue(long long value) { if (value == m_Value) return; m_Value = value; emit valueChanged(value); } #### main.qml import QtQuick 2.11 import QtQuick.Window 2.11 import QtQuick.Controls 2.11 Window { width: 640 height: 480 visible: true title: qsTr("QML Signals and slots example - Raymii.org") MenuBar { width: parent.width Menu { title: qsTr("File") MenuItem { text: qsTr("Exit") onTriggered: Qt.quit(); } } } Column { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter spacing: 20 Text { id: info width: parent.width * 0.9 wrapMode: Text.WordWrap text: "QML / C++ binding via signals and slots example program, by Raymii.org. License: GNU GPLv3" } Text { id: labelCount // C++ method Counter::value(). Bound via Q_PROPERTY, updates automatically on change text: "Counter: " + MyCounter.value + "." } Text { property int changeCount: 0 id: labelChanged text: "Count has changed " + changeCount + " times." // Receive the valueChanged NOTIFY Connections { target: MyCounter onValueChanged: { ++labelChanged.changeCount } } } Row { spacing: 20 Button { text: qsTr("Increase Counter") onClicked: ++MyCounter.value } Button { text: qsTr("Set counter to 10") // C++ method Counter::setValue(long long), bound via Q_PROPERTY onClicked: MyCounter.setValue(10) } Button { text: qsTr("Reset") onClicked: { // C++ method Counter::setValue(long long), bound via Q_PROPERTY MyCounter.setValue(0) } } } Row { spacing: 20 Text { id: setText text: qsTr("Enter counter value: ") } Rectangle { width: setText.width height: setText.height border.width: 1 border.color: "black" TextInput { id: counterInput focus: true text: MyCounter.value } } // Bi-directional binding, entering a number in the textarea updates the // C++ class, if the C++ class is updated, the textarea is updated as well. Binding { target: MyCounter property: "value" value: counterInput.text } } } } ### Build / Make To create the above code, first create a build folder outside of the project: cd /tmp mkdir build-qmlexample cd build-qmlexample Run `qmake`, replace the path (`/home/remy/tmp/qt/qml_cpp_signal_example/`) to your project path: qmake /home/remy/tmp/qt/qml_cpp_signal_example/qmlcppsignalexample.pro -spec linux-g++ CONFIG+=release && make qmake_all This example uses `qmake`, but there should be no trouble using `cmake`. Not using anything fancy here. When `qmake` has finished, you can run `make` to build the project: make -j4 After a short while, the binary should be available: $ file qml_cpp_signal_example qml_cpp_signal_example: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f884f57b90ebf05b51551d42cef5ca3ee52037b4, for GNU/Linux 3.2.0, with debug_info, not stripped Run it from the commandline: ./qml_cpp_signal_example ### QT Webassembly Demo For fun I [compiled][7] the example application to [webassembly][6]. Run it [here][5] or, if it loads, an `iframe` below: