Integrating with C++
Last updated
Last updated
In order to demonstrate the connection between C++ and QML in Qt for MCUs, we will create a simple Counter
singleton holding an integer value. Notice that we start from a struct
and not a class
. This is common practice in Qt Quick Ultralite.
The singleton will be used from a small UI as shown below.
The Counter
struct provides a property, value
, as well as methods for changing the value, increase
and decrease
, as well as a reset
method. It also provides a signal, hasBeenReset
.
Coming from Qt, this looks odd. This is where Qt for MCUs shows the main differences. There is no QObject
base class or Q_OBJECT
macro, instead a new set of classes from the Qul
is used. In this particular case, the base class is the Qul::Singleton
class, creating a globally accessible singleton in the QML world. We also use the Qul::Signal
class to create a signal and the Qul::Property
class to create a property. All public, non-overloaded member functions are exposed to QML automatically.
To create an element that can be instantiated from QML, instead of a singleton, use the `Qul::Object` base class.
The struct is then exposed to QML using the CMake macro qul_target_generate_interfaces
. Below you can see the CMakeLists.txt
, based on the file generated by Qt Creator, with the counter.h
and counter.cpp
files added.
Now, let's continue with the implementation of the Counter
struct. First up, for the value
property, we use the value
and setValue
functions to access and modify the actual value. In our case, the property holds and int
, but just as for the ordinary QML engine, types are mapped between C++ and QML.
This is used in the constructor, shown below, that sets the initial value to zero.
The increase
and decrease
functions look similar. They use the getter and setter instead of interacting directly with the value.
Counter
also has a signal. The signal is represented by the Qul::Signal
instance named hasReset
. The signal takes a function signature as template argument, so to create a signal carrying an integer, create a Qul::Signal<void(int)>
. In this case, the signal does not carry any values, so it is defined as a void(void)
. To emit the signal, we simply call it as if it was an ordinary function as shown in the reset
function below.
The QML code produces the simple user interface shown below.
We will look at the UI in three parts. First, the basic structure, and bindings to Counter.value
:
As you can tell, the Text
element's text
property is bound to the Counter.value
as in all QML.
Now, let's look at the left side buttons. These are used to invoke the C++ methods provided via the Counter
singleton. The PlainButton
is a QML element that we use to create these simple buttons. It lets you set the text, background color and a handler for the clicked
signal. As you can tell, each button calls the corresponding method on the Counter
singleton.
The buttons on the right modify the Counter.value
directly from QML. This is possible to do, but invisible to C++. There is no simple way for C++ to monitor if a property has changed, so if a C++ reaction is needed, it is recommended to use a setter method, rather than directly modifying the property value.
This shows how to provide a singleton from C++ and how to make function calls, emit signals, and share state (properties) between C++ and QML.
The CMakeLists.txt
file may look familiar to you, but there are some tips and tricks that we need to discuss.
First of all, in order to expose a C++ class to QML, use the qul_target_generate_interfaces
, e.g:
The other half, the QML files, are added using the qul_target_qml_sources
macro. If you have multiple QML files, simply list them one by one as shown below:
Another interesting aspect is that we are building a C++ project without writing a main
function. This is taken care of by the app_target_default_main
macro that adds a reference main implementation to the project. You can of course replace this with a custom main
function if you need more control.
Finally, the libraries linked to are not the standard Qt ones, but the Qul::
ones, e.g:
Links Further reading at qt.io: