How to Integrate C++ and QML? − Register C++ enum to QML

Qt Qml development
2022-07-13
11 minutes
How to integrate C++ and Qml? Registering Enums

We continue our series of blog posts about integrating QML and C++. Last time we covered registering C++ types as singletons in QML.

 

Learn How to Integrate C++ and QML by Registering Enums

Today we will proceed with another frequently used technique in Qt software development ― utilizing C++ enums in QML code. In this article we continue using the most recent method of integrating C++ code and QML― CMake command qt_add_qml_module().

In the meantime, you can check out what we can offer you in C++ Development services.

 

Enum Vs. Enum Class In C++

We assume the majority of developers are familiar with the idea of enum in most programming languages, that is why we will not stop on it for too long.
Although, if you are not sure what enum is, please, get acquainted with this topic first and then proceed reading the article. Fortunately, it is a simple concept that can be understood in a few minutes.
However, not all developers know that C++ has not only usual enums, but also something called enum c++ class.
What are those and what do we need them for?
An idea of enum c++ class comes from constraints of regular enum and them being quite unsafe to use.
For example, using regular C++ enum you may easily have a following code:</>

 

#include <iostream>

enum Color { RED, BLUE, GREEN };
enum Position { TOP, BOTTOM, LEFT, RIGHT };

int main()
{
    auto color = RED;
    auto position = TOP;

    if (color == position) {
        std::cout << "equals" << std::endl;
    }
}

Although, most probably compiler would produce a warning about comparing different types, you will see a desired “equals” in the output, meaning that comparison resulted in true:

 

main.cpp: In function 'int main()':
main.cpp:11:18: warning: comparison between 'enum Color' and 'enum Position' [-Wenum-compare]
     if (color == position) {
                  ^~~~~~~~
equals

In order to solve this problem, you may use enum class because there is no implicit conversion from enum classes to integers while there is one with regular enums. Actually, this is one more advantage of class enum which provides more type safety.
However, if you really want to convert class enum to integer, you may do it explicitly:

 

static_cast<int>(Color::RED);

Another situation when enum class is useful is when you would like to have the same values in different enums. A code with the following definition of regular enums would not compile:

 

enum CarColor { RED, BLUE, GREEN };

enum LightColor { RED, BLUE, GREEN };

Compiler would produce an error:

 

main.cpp:5:19: error: redeclaration of 'RED'
 enum LightColor { RED, BLUE, GREEN };
                   ^~~
main.cpp:3:17: note: previous declaration 'CarColor RED'
 enum CarColor { RED, BLUE, GREEN };
                 ^~~
main.cpp:5:24: error: redeclaration of 'BLUE'
 enum LightColor { RED, BLUE, GREEN };
                        ^~~~
main.cpp:3:22: note: previous declaration 'CarColor BLUE'
 enum CarColor { RED, BLUE, GREEN };
                      ^~~~
main.cpp:5:30: error: redeclaration of 'GREEN'
 enum LightColor { RED, BLUE, GREEN };
                              ^~~~~
main.cpp:3:28: note: previous declaration 'CarColor GREEN'
 enum CarColor { RED, BLUE, GREEN };
                            ^~~~~

Usually, such issues were solved by using different names of enum values. I.e. C_RED and L_RED.
However, if you use enum class you may have the same names for values and everything will compile successfully:

 

enum class CarColor { RED, BLUE, GREEN };

enum class LightColor { RED, BLUE, GREEN };

 

Exposing Enums To QML

Although QML allows declaring enums directly inside .qml files, today we will discuss another approach.
It is possible to declare an enum in C++ part of the application and use it both in C++ and QML.
We will use qt_add_qml_module command so the process would look similar to what we have done in our previous blog posts of this series.
First, we have to declare an enum inside a .cpp file. Let us use our well known ImportantClass. We would use a pretty classic example of enums ― day of the week.

 

#include <QObject>
#include <QtQml/qqml.h>

class ImportantClass : public QObject
{
    Q_OBJECT
    QML_ELEMENT
public:
    explicit ImportantClass(QObject *parent = nullptr);

    enum Day {
        MONDAY,
        TUESDAY,
        WEDNESDAY,
        THURSDAY,
        FRIDAY,
        SATURDAY,
        SUNDAY,
    };
    Q_ENUM(Day)

public slots:
    void myFun(ImportantClass::Day day) const {
        qDebug() << day;
        // ... very smart code
    }
};

As you may see, we add QML_ELEMENT macro in order to expose C++ class to the QML.
In addition, we have to use Q_ENUM macro with the name of the enum that we have introduced:

 

enum Day {
       MONDAY,
       TUESDAY,
       WEDNESDAY,
       THURSDAY,
       FRIDAY,
       SATURDAY,
       SUNDAY,
   };
   Q_ENUM(Day)

The following steps are similar to ones we had in our previous articles. We need to use qt_add_qml_module in CMakeLists.txt file.

 

qt_add_qml_module(appEnumExample
    URI EnumExample
    VERSION 1.0
    QML_FILES
        main.qml
    SOURCES
        ImportantClass.h
        ImportantClass.cpp
)

Do not forget to specify a correct URI in order imports to work correctly. The last step you have to do is to import this class using the provided URI.
As usual, we have created a simple example in order to show how all the mentioned things work in practice.

Here is an example of a program with buttons changing text of a label:

 

import QtQuick
import QtQuick.Controls

import EnumExample

Window {
  id: root

  signal newDaySelected(var day) // day - enum Day.

  width: 640
  height: 480
  visible: true
  title: qsTr("Hello World")

  Text {
    id: selectedDay

    anchors {
      bottom: row.top
      horizontalCenter: row.horizontalCenter
      bottomMargin: 30
    }

    text: "Select day"
    font.pixelSize: 20
  }

  ListView {
    id: row

    height: 30
    width: contentWidth

    anchors.centerIn: parent

    model: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

    delegate: Button {
      id: rowDelegate
      height: 30
      width: 80

      text: modelData

      onClicked: {
        root.newDaySelected(index) //<-- Remember that enum can be converted to int.
        importantObject.myFun(index)
      }
    }

    orientation: ListView.Horizontal
  }

  ImportantClass {
    id: importantObject
  }

  onNewDaySelected: function(day) {
    switch (day) {
    case (ImportantClass.MONDAY):
      selectedDay.text = "Selected day: Monday"
      break
    case (ImportantClass.TUESDAY):
      selectedDay.text = "Selected day: Tuesday"
      break
    case (ImportantClass.WEDNESDAY):
      selectedDay.text = "Selected day: Wednesday"
      break
    case (ImportantClass.THURSDAY):
      selectedDay.text = "Selected day: Thursday"
      break
    case (ImportantClass.FRIDAY):
      selectedDay.text = "Selected day: Friday"
      break
    case (ImportantClass.SATURDAY):
      selectedDay.text = "Selected day: Saturday"
      break
    case (ImportantClass.SUNDAY):
      selectedDay.text = "Selected day: Sunday"
      break
    }
  }
}

Let us take a closer look at the specific parts of the program.
Firstly, we have to import our class using the provided URI:

 

import EnumExample

And then create an instance of ImportantClass to use:

 

ImportantClass {
  id: importantObject
}

Going pretty good, now we may use our importantObject. There is a row of buttons that change the text above depending on the button you click on.

 

hello world

 

Here is the implementation of each button click. You may see that it emits a signal with an index of a button.
Though the signal accepts enum as parameter, enums can be converted to integers. It also calls a method of importantObject which simply prints the value of the provided enum.

 

onClicked: {
    root.newDaySelected(index) //<-- Remember that enum can be converted to int.
    importantObject.myFun(index)
}

Output:

 

ImportantClass::FRIDAY

That is actually it. This is a very primitive example of utilizing enums, however you may design a complicated logic and it would perfectly work in Qt.

 

Benefits of Coding in QML

QML (Qt Modeling Language) has become a popular choice for developers due to its fast development time and powerful features.

 

Benefits of Coding in QML

This language makes it possible to reduce the amount of code needed by up to 60% compared to other programming languages. Now, we will look at some of the advantages of coding with QML.

Coding in QML offers several benefits for developers when compared to other languages:

 

Simplicity

Learning QML + JavaScript is much easier than writing code in C++ or other programming languages. Additionally, coding with QML significantly reduces the amount of code needed due to its simple syntax structure.

 

Time-Saving Features

Features like states, signals and slots, and property bindings are great time-savers when coding with QML.

These features can be activated with just a few lines of code and allow developers to easily create complex applications without writing lengthy amounts of code.

 

Animations Made Simple

Animating different elements within an application has never been easier, thanks to QML’s Animation components. Animating properties in your elements can be done quickly and accurately with these components, saving time and money during the development process.

 

Flexibility & Extensibility

Need to create a functionality or feature?

With QML, you can easily extend C++ object within your app without having to create a new entirely type – saving you even more time!

 

High-Performance Rendering Qml Engine

The renderer used by Qt Quick offers excellent performance as it takes advantage of hardware acceleration via a scene graph which powers both kits and high-performance games alike.

 

When To Choose C++ Over QML?

For certain scenarios, you may have to opt for native C++ code instead of interpreted QML/JavaScript code.

The primary benefit here is that native code will provide superior performance under data-intense conditions compared to interpreted languages like QML or JavaScript.

Additionally, since code written in C++ is compiled into object code rather than interpreted, this raises security and stability levels much higher than their interpreted counterparts since there are no bugs introduced through interpretation errors during runtime.

Another advantage comes from how flexible Qt’s components can be when using the native ones developed in C++; they offer different (and sometimes more) features than similar types available in QML.

For instance, advanced networking capabilities might only be present in these components if developed using native codes such as those found within C++ or Objective-C/Swift frameworks within iOS projects.

Finally, mixing both native code (such as Java Native Invocation on Android) and object-oriented classes from not just Qt but other frameworks allows cross-platform compatibility while providing support for additional features like those seen in more modernized mobile devices such as GPS tracking, motion sensors, and cameras simply by bridge them across all supported platforms into one application seamlessly.

This is only possible thanks to compiling native source codes written in any language your target device accepts, including Java/Kotlin on Android, Swift/Objective-C on iOS, or Windows Runtime Component (Object Pascal) on Windows 10 Mobile devices.

 

3 Thoughts on Passing Enum Properties between QML and C++

Enums are a popular way of representing sets of discrete values, and they are frequently used in both C++ and QML to achieve better code readability and maintainability.

 

Passing Enum Properties between QML and C++

 

However, when trying to pass enum properties from C++ to QML, there can be some complications that should be taken into account.

So now we will look at three major pitfalls to watch out for and discuss how to overcome them.

 

Nesting Enums in Classes

One approach for passing enum properties from C++ to QML is by nesting the enum within a class and then exposing the class object directly to QML through the `Q_PROPERTY` macro.

While this approach does make it possible for developers to use the enum from both sides of the equation, it also introduces a few issues.

For one thing, using an extra class just for the sake of being able to export an enum context property is not really ideal – instead, it would be preferable if that class could have more purpose or meaning in its own right.

Additionally, once an enum has been declared inside a class, it cannot be forward-declared anymore, which can have a significant impact on creating header dependencies if you do this several times over a large codebase.

 

Using The `Q_ENUM_NS` Macro

Luckily Qt has provided us with another option: Instead of nesting enums within classes, developers can create namespaces specifically for declaring enums within.

This makes it possible for enums to still be accessed from both C++ as well as QML while also allowing them to retain their ability to be forward declared easily – thus making a large new project much easier on maintenance when changes need to occur down the line.

The `Q_ENUM_NS` macro makes it easy for developers to declare a namespaceenum QML type that can immediately be picked up by both languages – ultimately making integration much smoother than otherwise would have been achievable with nested classes alone.

 

Meta Object & Reflection

Lastly, Qt gives developers access to additional flexibility via the Meta Object System (MOS).

With MOS, Qt applications gain access to meta information about a QML object during runtime, which can then be used together with reflection functions like `property()` or `invokeMethod().`

These functions make it easy for developers who are familiar with C++’s reflection capabilities in general or Qt’s MOS features in particular to find new ways of exchanging data between different parts of their application – including passing enum types from C++ on over into QML-based user interfaces.

 

Summing It Up

Take advantage of our cross-platform solutions and experience! Qt is an innovative and stable framework that enables you to develop fast, reliable, and responsive software for desktop, mobile, and embedded environments.

Scythe Studio offers comprehensive software services – from architecture design to body and team leasing – so you can turn ideas into reality without worry.

Contact us today with your questions or brilliant ideas to get started!

Scythe-Studio - Blog Redactors

Scythe Studio Blog Redactors

Need Qt QML development services?

service partner

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 capabilities

Latest posts

[ 134 ]