In a new Qt project, it is often desirable to mix C++ and QML code. At least in our experience, it is rare that a project is either pure C++ or pure QML. The Qt Documentation has some overview of the available options of mixing the two. This documentation was somewhat lacking in the past, but has vastly improved for the recently introduced version 5.4.
In particular, we look at an example of integrating a C++ object as a context property into QML. See Embedding C++ Objects into QML with Context Properties in the official documentation that discusses this topic.
The sample code from this project is available on Github.
Project Setup
In Qt Creator, chooce a project of type Qt Quick Application
. This allows one to use C++ together with QML.
General Concept
There are different ways to send signals from C++ to QML and back. In this article, we show how to do this by embedding a C++ class directly into QML. This has the advantage that no Qt::connect
connections need to be set-up manually.
In our example, we have a Receiver
class that is implemented in C++. This
class defines a signal sendToQml
and a slot receiveFromQml
. Both have an integer
parameter. The signal is sent to QML, and the slot is invoked from QML.
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
class Receiver : public QObject
{
Q_OBJECT
public:
explicit Receiver(QObject *parent = 0);
signals:
void sendToQml(int count);
public slots:
void receiveFromQml(int count);
};
#endif // RECEIVER_H
To invoke the C++ slot, a QML program can then simply call the respective function on the embedded object. In order to receive the C++ signal, the QML program can use the Connections class.
import QtQuick 2.2
import QtQuick.Window 2.1
Window {
Connections {
target: receiver
onSendToQml: {
console.log("Received in QML from C++: " + count)
}
}
MouseArea {
anchors.fill: parent
onClicked: {
receiver.receiveFromQml(42);
}
}
}
Embedding the Object
We have seen how to connect C++ and QML, but what is left is the task of
actually embedding the C++ object into QML. This is done by setting a context
property. In order to do this, one needs to get the rootContext
from the QML
engine, and set its property.
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "receiver.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Receiver receiver;
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("receiver", &receiver);
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
As mentioned in the overview, there is no need to manually connect the C++
receiver
objects signals and slots to QML. Setting the context property is
enough for embedding.
Naming Conventions
Please note that Qt has a specific naming convention that needs to be respected
for the connection to work. In our example, the Receiver
class defines a
signal that is called sendToQml
. The Connections
object can then access
this signal with the name onSendToQml
. The signal name must be lower case.
The other end can then connect the signal by prepending the word on
and
continue with camel case.
Full Example
The complete source code is on Github. Please clone the code and try to run it. Instructions how to run it can be found in the project’s readme.
When everything is setup correctly, the program outputs a message at startup that it received a value from C++, and when you click in the window, another message should appear that a value was sent to C++. Both messages are sent to the debug console.