Qt Graphs vs Qt Charts. Objective Take on the New Data Visualization Solution
The Qt Graphs is a powerful module of the Qt framework that enables developers to create interactive, both static and […]
In our previous post about Registering C++ class as a singleton to QML, we explained why it is important to integrate QML and C++ and covered the basics of this mechanism. In this article, we will go into more detail and discuss a specific real-life example of integrating elements that can be instantiated one-time only (aka. singletons).
If you have come across this post because you are looking for software development help then we encourage you to learn more about our C++ development services.
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 common to utilize this pattern in QML as well. A controller that holds some dynamic (not only) properties like signals and slots and supplies utility functions is a great candidate for a singleton pattern class object. However, you should be careful about using this pattern in applications that apply multi-threading.
Qt framework provides multiple ways of registering C++ classes in QML and each of them has its use. QML Javascript can also be used for this purpose. Today we will discuss the most recent one using qt_add_qml_module(). It was introduced in Qt 6.2 and is widely recommended for registering QML type.
First of all, we need to create a new 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 about Exposing object and register C++ class to QML. The only difference is the Q_SINGLETON macro, it is 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 the 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. Qt Creator may sometimes complain about wrong imports, but this is not true. Make sure you update Qt Creator to the newest version or reset the QML type model. Then it is more probable that your own QML modules will be recognized properly in your code editor.
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 the first button increases the value of ImportantNumber by 1:
onClicked: { ImportantClass.importantNumber += 1 }
And clicking the second button sets its text to the 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 an error. Remember about the Q_SINGLETON macro? That is the magic word that extends your QML code with C++ singleton visible as QML object.
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 QML and C++ we suggest reading official Qt documentation as well as our other blog posts in „How to integrate QML and C++” series.
Trying to decide whether the Singleton pattern is appropriate can be difficult. It can seem like a great idea at first, but it can also lead to unexpected issues later on.
There should be precisely one instance of a class accessible from a familiar or client access point.
It should be feasible to broaden the single instance through sub-classing without client code amendment.
However, these points may not provide enough practical guidance for assessing if a Singleton is really suitable for your project. You have to see your circumstances.
A Singleton can act as a global object and provide access from anywhere within the program. But since it is global, it cannot be modified outside of itself. Thus, it serves as a means to safeguard globals from outside interference.
Whenever we model real-life entities through our programs, reasoning about them becomes much easier. For instance, registration offices, global timers, and factories responsible for unique IDs are all examples of Singletons.
This creates a correspondence between programming abstractions and reality which helps in simplifying the understanding of the program for clients and coders alike.
To avoid such issues, it is important to ensure that the class you are creating is exactly a Singleton. Also, when designing this pattern with testing in mind, remember to pass the Singleton as a parameter wherever possible using dependency injection. This will help you identify and resolve issues before it becomes too late.
Singleton classes have many potential uses, including cache memory, database connections, drivers, and logging. Here are some major applications:
The usage of singleton classes is dependent on the situation and requirements. These objects are often used to prevent concurrent access to a certain class. This is especially useful in hardware resource utilization limitations, such as for hardware printers where the print spooler can be made a singleton to avoid deadlocks arising from multiple simultaneous accesses.
A logger instance created with a singleton class could also prove beneficial when it comes to log files. If there are multiple clients accessing the same logging utility class, issues may occur regarding concurrent access to the same file due to multiple class instances being created. Utilizing a global point of reference via the singleton pattern would help ensure that no two users simultaneously gain access to this utility.
Singleton classes are perfect for configuration files because they improve performance by preventing repeated read accesses from multiple users. Moreover, this creates only one instance of the configuration file, which can be accessed concurrently without any data overload or conflict between different reads by various clients from an application’s end.
The data obtained from this single instance would then be utilized repeatedly across all future calls for better memory management and efficiency.
Finally, caches are another type of service that can benefit from using singleton objects as they provide a global point of reference for their hosting applications, which makes accessing stored information easier and more efficient over time with minimal resources required for the operation.
When a C++ class is registered as a singleton to QML, it creates a static instance. This is the most commonly used approach. However, it may not be memory efficient. If the class is not called upon, the Singleton will remain present, and if the Singleton is “heavy,” it can take up unnecessary space in RAM.
Qt QML and C++ pair is the perfect combination for those looking for a flexible, innovative, and reliable way to develop cross-platform applications. With Qt, you can easily deploy your app across multiple platforms with one codebase, ensuring efficient performance and bug-free coding.
Our team of Qt QML development experts can help you every step of the way – from requirements engineering, actual development, debugging, and migration to deployment. With us on your side, you will never have to worry about making a successful application. Try Scythe Studio and see our Qt consulting services – we promise you won’t regret it!
Let's face it? It is a challenge to get top Qt QML developers on board. Help yourself and start the collaboration with Scythe Studio - real experts in Qt C++ framework.
Discover our capabilitiesThe Qt Graphs is a powerful module of the Qt framework that enables developers to create interactive, both static and […]
Embedded systems are everywhere, from household appliances to industrial machinery. At the heart of these systems is embedded firmware, the […]
Outsourcing embedded systems development has become an increasingly popular strategy for businesses looking to enhance efficiency, reduce costs, and leverage […]