Nova Flow OS
Qt6 QML Book
Qt6 QML Book
  • Qt6 QML Book
    • Preface
      • Welcome!
      • Acknowledgements
      • Authors
    • Meet Qt
      • Qt and Qt Quick
      • Qt Building Blocks
      • Qt 6 Introduction
    • Getting Started
      • Quick Start
      • Installing Qt 6 SDK
      • Hello World
      • Application Types
      • Summary
    • Qt Creator IDE
      • Qt Creator IDE
      • The User Interface
      • Registering your Qt Kit
      • Managing Projects
      • Using the Editor
      • Locator
      • Debugging
      • Shortcuts
    • Quick Starter
      • Quick Starter
      • QML Syntax
      • Core Elements
      • Components
      • Simple Transformations
      • Positioning Elements
      • Layout Items
      • Input Elements
      • Advanced Techniques
    • Fluid Elements
      • Fluid Elements
      • Animations
      • States and Transitions
      • Advanced Techniques
    • Qt Quick Controls
      • UI Controls
      • Introduction to Controls
      • An Image Viewer
      • Common Patterns
      • The Imagine Style
      • Summary
    • Model View
      • Model View-Delegate
      • Concept
      • Basic Models
      • Dynamic Views
      • Delegate
      • Advanced Techniques
      • Summary
    • Canvas
      • Canvas Element
      • Convenience API
      • Gradients
      • Shadows
      • Images
      • Transformation
      • Composition Modes
      • Pixel Buffers
      • Canvas Paint
      • Porting from HTML5 Canvas
    • Shapes
      • Shapes
      • A Basic Shape
      • Building Paths
      • Filling Shapes
      • Animating Shapes
      • Summary
    • Effects
      • Effects in QML
      • Particle Concept
      • Simple Simulation
      • Particle Parameters
      • Directed Particles
      • Affecting Particles
      • Particle Groups
      • Particle Painters
      • Graphics Shaders
      • Shader Elements
      • Fragment Shaders
      • Wave Effect
      • Vertex Shader
      • Curtain Effect
      • Summary
    • Multimedia
      • Multimedia
      • Playing Media
      • Sound Effects
      • Video Streams
      • Capturing Images
      • Summary
    • Qt Quick 3D
      • Qt Quick 3D
      • The Basics
      • Working with Assets
      • Materials and Light
      • Animations
      • Mixing 2D and 3D Contents
      • Summary
    • Networking
      • Networking
      • Serving UI via HTTP
      • Templates
      • HTTP Requests
      • Local files
      • REST API
      • Authentication using OAuth
      • Web Sockets
      • Summary
    • Storage
      • Storage
      • Settings
      • Local Storage - SQL
    • Dynamic QML
      • Dynamic QML
      • Loading Components Dynamically
      • Creating and Destroying Objects
      • Tracking Dynamic Objects
      • Summary
    • Javascript
      • JavaScript
      • Browser/HTML vs Qt Quick/QML
      • JS Language
      • JS Objects
      • Creating a JS Console
    • Qt C++
      • Qt and C++
      • A Boilerplate Application
      • The QObject
      • Build Systems
      • Common Qt Classes
      • Models in C++
    • Extending QML
      • Extending QML with C++
      • Understanding the QML Run-time
      • Plugin Content
      • Creating the plugin
      • FileIO Implementation
      • Using FileIO
      • Summary
    • Qt for Python
      • Qt for Python
      • Introduction
      • Installing
      • Building an Application
      • Limitations
      • Summary
    • Qt for MCUs
      • Qt for MCUs
      • Setup
      • Hello World - for MCUs
      • Integrating with C++
      • Working with Models
      • Summary
    • About
      • Readme
      • License
Powered by GitBook
On this page
  • The C++
  • The QML
  • Revisiting the CMake file
  1. Qt6 QML Book
  2. Qt for MCUs

Integrating with C++

PreviousHello World - for MCUsNextWorking with Models

Last updated 9 months ago

The C++

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.

#ifndef COUNTER_H
#define COUNTER_H

#include <qul/singleton.h>
#include <qul/property.h>
#include <qul/signal.h>

class Counter : public Qul::Singleton<Counter>
{
public:
    Counter();

    Qul::Property<int> value;

    void increase();
    void decrease();

    void reset();
    Qul::Signal<void(void)> hasBeenReset;

};

#endif // COUNTER_H

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.

qul_target_generate_interfaces(cppintegration counter.h)

This is used in the constructor, shown below, that sets the initial value to zero.

Counter::Counter()
{
    value.setValue(0);
}

The increase and decrease functions look similar. They use the getter and setter instead of interacting directly with the value.

void Counter::increase()
{
    value.setValue(value.value()+1);
}

void Counter::decrease()
{
    value.setValue(value.value()-1);
}

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.

void Counter::reset()
{
    std::cout << "Resetting from " << value.value() << std::endl;
    value.setValue(0);
    hasBeenReset();
}

The QML

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:

import QtQuick

Rectangle {
    width: 480
    height: 272

    Column {
        // Left buttons goes here
    }

    Column {
        // Right buttons goes here
    }

    Text {
        anchors.centerIn: parent
        text: 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.

Column {
    x: 10
    y: 10
    spacing: 5
    PlainButton {
        text: "+"
        onClicked: Counter.increase()
    }
    PlainButton {
        text: "reset"
        onClicked: Counter.reset()
    }
    PlainButton {
        text: "-"
        onClicked: Counter.decrease()
    }
}

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.

Column {
    x: 350
    y: 10
    spacing: 5
    PlainButton {
        color: "orange"
        text: "++"
        onClicked: Counter.value += 5;
    }
    PlainButton {
        color: "orange"
        text: "100"
        onClicked: Counter.value = 100;
    }
    PlainButton {
        color: "orange"
        text: "--"
        onClicked: Counter.value -= 5;
    }
}

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.

Revisiting the CMake file

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:

qul_target_generate_interfaces(cppintegration counter.h)

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:

qul_target_qml_sources(cppintegration cppintegration.qml PlainButton.qml)

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.

app_target_default_main(cppintegration cppintegration)

Finally, the libraries linked to are not the standard Qt ones, but the Qul:: ones, e.g:

target_link_libraries(cppintegration
    Qul::QuickUltralite
    Qul::QuickUltralitePlatform)

Links Further reading at qt.io:

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
Integrate C++ and QML