--- title: "QML2 to C++ and back again, with signals and slots" date: 2014-11-23 categories: ["Qt"] source: https://andrew-jones.com/blog/qml2-to-c-and-back-again-with-signals-and-slots/ --- # QML2 to C++ and back again, with signals and slots Earlier this week, I posted an example of integrating [QML2 and C++](/blog/qml2-to-c-and-back-again/). In it I showed how to call a C++ method from QML, but finished my post with this statement. > I’m still new to Qt, so this may not be the best way. It looks like you can also use signals, which would probably be better as it means your QML application isn’t tied to your C++ implementation, but I haven’t yet got that working. I have now found a way to use signals and slots to do this, which I will describe here. ## Signals and Slots [Signals and Slots](http://qt-project.org/doc/qt-5/signalsandslots.html) are a feature of Qt used for communication between objects. When something happens to an object, it can emit a signal. Zero or more objects can listen for this signal using a slot, and act on it. The signal doesn't know if anything is listening to it, and the slot doesn't know what object called it. This allows you to design and build a [loosely coupled](http://en.wikipedia.org/wiki/Loose_coupling) application, giving you the flexibility to change, add or remove features of one component without updating all its dependencies, so long as you continue to emit the same signals and listen on the same slots. You can see why this might be useful in GUI programming. When a user enters some input, you may want to do a number of things with it. Maybe you want to update the GUI with a progress bar, then kick off a function to handle this input. This function might emit a signal letting others know its progress, which the progress bar could listen to and update the GUI. And so on. Even outside of GUI programming this could be useful. You might have an object watching the filesystem for changes. When a change happens, you could emit a signal to let other objects know about this change. One object might run a process against this file, while another object updates a cache of the filesystem. ## The example application I'm going to create the same example application as I did before. It will contain a text field and a button. You enter some text in the text field, and once you click the button the text will be converted to upper-case. The conversion to upper-case will be done in C++, and the interface drawn in QML2. The source of the finished application is available [on GitHub](https://github.com/andrewrjones/qml2-to-cpp-and-back-signals). ![A graphical user interface window displaying a text input field with the text "hello, Qt" and a button labeled "Uppercase me!".](https://andrew-jones.com/images/posts/qml-to-cpp/app-start.png) ## Emitting a signal from QML and listening to it from C++ To create a signal in QML, simply add the following line to the object which will emit the signal. ``` signal submitTextField(string text) ``` Here I have created a signal, `submitTextField`, which will pass a string as an argument to any connecting slots (if they choose to receive it). I'm going to add this signal to the `Window`. I will emit the signal when the button is pressed, passing the value of the text field. Here is the full QML document. ``` import QtQuick 2.3 import QtQuick.Window 2.2 import QtQuick.Controls 1.2 Window { signal submitTextField(string text) visible: true width: 360 height: 360 MouseArea { anchors.fill: parent onClicked: { Qt.quit(); } } TextField { id: textField1 x: 31 y: 169 placeholderText: qsTr("Enter some text...") } Button { x: 193 y: 167 text: qsTr("Uppercase me!") onClicked: // emit the submitTextField signal submitTextField(textField1.text) } } ``` We can run that and click the button. The signal is being emitted, but because no slots are listening to it nothing happens. Let's create a C++ class to listen to this signal. I'm going to call it `HandleTextField`, and it will have a slot called `handleSubmitTextField`. The header file looks like this. ``` #ifndef HANDLETEXTFIELD_H #define HANDLETEXTFIELD_H #include #include class HandleTextField : public QObject { Q_OBJECT public: explicit HandleTextField(QObject *parent = 0); public slots: void handleSubmitTextField(const QString& in); }; #endif // HANDLETEXTFIELD_H ``` The class file has a simple implementation for `handleSubmitTextField`. ``` #include "handletextfield.h" /* * This class handles interactions with the text field */ HandleTextField::HandleTextField(QObject *parent) : QObject(parent) { } void HandleTextField::handleSubmitTextField(const QString &in) { qDebug() << "c++: HandleTextField::handleSubmitTextField:" << in; } ``` To connect the QML signal to the C++ slot, we use `QObject::connect`. Add the following to `main.cpp`. ``` HandleTextField handleTextField; QObject *topLevel = engine.rootObjects().value(0); QQuickWindow *window = qobject_cast(topLevel); // connect our QML signal to our C++ slot QObject::connect(window, SIGNAL(submitTextField(QString)), &handleTextField, SLOT(handleSubmitTextField(QString))); ``` We need an instance of `HandleTextField`, and the QML Window object. Then we can connect the windows `submitTextField` signal to the `handleSubmitTextField` slot. Running the application now and you should get a debug message showing the text being passed to C++. ## Emitting a signal from C++ and listening to it from QML Now we want to convert the string to upper-case and display it in the text field. Lets create a signal in our C++ class by adding the following to the header file. ``` signals: void setTextField(QVariant text); ``` Then change the `handleSubmitTextField` function to emit this signal with the upper-cased text. ``` void HandleTextField::handleSubmitTextField(const QString &in) { qDebug() << "c++: HandleTextField::handleSubmitTextField:" << in; emit setTextField(in.toUpper()); } ``` Notice we are passing the text as a [`QVariant`](http://qt-project.org/doc/qt-5/qvariant.html). This is important, for the reasons well described in this [Stack Overflow answer](http://stackoverflow.com/a/8840945/755720). > The reason for the QVariant is the Script based approach of QML. The QVariant basically contains your data and a desription of the data type, so that the QML knows how to handle it properly. That's why you have to specify the parameter in QML with String, int etc.. But the original data exchange with C++ remains a QVariant We now need a slot in QML to connect this signal to. It will handle the updating of the text field, and is simply a function on the `Window`. ``` // this function is our QML slot function setTextField(text){ console.log("setTextField: " + text) textField1.text = text } ``` Finally we use `QObject::connect` to make the connection. ``` QObject::connect(&handleTextField, SIGNAL(setTextField(QVariant)), window, SLOT(setTextField(QVariant))); ``` Run the application, and you should now see your text be converted to upper-case. ![A window with a text input box containing the words "HELLO, QT" and a button labeled "Uppercase me!".](https://andrew-jones.com/images/posts/qml-to-cpp/app-finish.png) ## Next steps It feels like more work to use signals and slots, instead of method calls. But you should be able to see the benefits of a loosely coupled application, especially for larger applications. I'm not sure a handler class for the text field is the best approach for this in practice. In a real application I think you would emit user actions in your GUI and have classes to implement your application logic without knowledge of where that data is coming from. As I gain more experience with Qt, I will update the example with the best practice. Check out the full application [on GitHub](https://github.com/andrewrjones/qml2-to-cpp-and-back-signals). Cover image by [Beverley Goodwin](https://www.flickr.com/photos/bevgoodwin/9675302995).