Introduction
In our previous post about integrating C++ and Qml we have explained why it is important to integrate C++ and Qml and covered the basics of this mechanism. In this article we will go into more details and discuss a specific real-life example of integrating elements which can be instantiated one-time only (aka. singletons).
Before we start, let us briefly explain what singleton is, for those who hear this term for the first time. Singleton is a software design pattern that restricts creating an object of a class to a single instance only. There are many use-cases for singletons. I.e. an object that holds a program state and can be accessed from different parts of the application. It is a common to utilize this pattern in Qml as well. A controller that holds some dynamic (not-only) properties and supplies utility functions is a great candidate for a singleton pattern class object. However, you should be careful about using this pattern in application that apply multi-threading.
Qt framework provides multiple ways of registering C++ classes in Qml and each of them has their use. Today we will discuss a most recent one using qt_add_qml_module(). It was introduced in Qt 6.2 and is widely recommended for registering new types in QML.
First of all, we need to create our C++ class we want to expose to QML.
#include <QObject>
#include <QtQml/qqml.h>
class ImportantClass : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_ELEMENT
Q_PROPERTY(int importantNumber READ importantNumber WRITE setImportantNumber NOTIFY importantNumberChanged)
public:
explicit ImportantClass(QObject *parent = nullptr);
void setImportantNumber(int num) {
if (num != m_importantNumber) {
m_importantNumber = num;
emit importantNumberChanged();
}
}
int importantNumber() const {
return m_importantNumber;
}
public slots:
double convertFahrenheit(int value) const {
return value * 1.8 + 32;
}
signals:
void importantNumberChanged();
private:
int m_importantNumber = 10;
};
Everything should look familiar to you, if you have read our previous post in the series. The only differences are Q_SINGLETON and Q_ELEMENT macros, they are important to register a singleton type properly. In ImportantClass we define an importantNumber property and a convertFahrenheit(value) function which, as you may guess from the name, converts provided value to Fahrenheit temperature scale.
Next we need to go to our CMakeLists.txt file and use qt_add_qml_module() command to register our class.
qt_add_qml_module(appSingletonExample
URI SingletonExample
VERSION 1.0
QML_FILES
main.qml
SOURCES
ImportantClass.h
ImportantClass.cpp
)
Provide a URI that you would like to use in the QML import statement. Then specify C++ files your class is in. Congratulations, you have successfully registered a singleton in Qml!
Let us create an example to check if everything works correctly. Here is a simple Qml application consisting of two buttons.
import QtQuick
import QtQuick.Controls
import SingletonExample
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Button {
id: importantNumberButton
anchors.centerIn: parent
width: 100
height: 50
text: ImportantClass.importantNumber
onClicked: {
ImportantClass.importantNumber += 1
}
}
Button {
anchors {
top: importantNumberButton.bottom
horizontalCenter: importantNumberButton.horizontalCenter
}
width: 100
height: 50
text: "Click to convert"
onClicked: {
text = ImportantClass.convertFahrenheit(ImportantClass.importantNumber)
}
}
}

One of them increases the mentioned-above importantNumber value and second calls convertFahrenheit() function on this value. QtCreator may sometimes complain about wrong imports, but this is not true. In addition, IDE will not provide autocomplete for registered types. We hope it will be fixed soon, because it does not save you from making subtle spelling mistakes which do not stop the execution of QML code, but result in having undefined values.
Next we need to import the registered type using import URI (import SingletonExample in our case). Now we may use ImportantClass in this QML file: Clicking on first button increases a value of ImportantNumber by 1:
onClicked: {
ImportantClass.importantNumber += 1
}
and clicking second button sets its text to Fahrenheit value of importantNumber:
onClicked: {
text = ImportantClass.convertFahrenheit(ImportantClass.importantNumber)
}
Note, that you do not have to initialize ImportantClass objects as it is done internally. Attempts to do that will result in error. Remember about Q_SINGLETON macro?
That is actually it. You have learned how to register singletons in QML from C++ using CMake qt_add_qml_module() command. If you want to learn about integrating C++ and QML we suggest reading official Qt documentation as well as our other blog posts in „How to integrate Qml and C++” series.
If you have any questions, do not hesitate to contact us! Our team of professional developers is always ready to help you provide the best possible software-development services.
Andrey Staikov
Qt Developer