Scythe Studio website employs cookies to improve your user experience (Read more)
How to integrate C++ and Qml? Registering Enums

How to integrate C++ and Qml? Registering Enums

Something about enums

Introduction

We continue our series of blog posts about integrating C++ and Qml. Last time we covered registering C++ types as singletons in Qml. 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().

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 class. What are those and what do we need them for?

An idea of enum 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 the whole 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.

You may find more information about integrating C++ and Qml in our previous blog posts. Stay tuned for new blog posts and do not hesitate to contact us in case you have any questions or you have a brilliant idea, but you do not know how to implement it in code.

Scythe-Studio - Blog Redactors

Scythe Studio Blog Redactors

Categories

Latest posts

Outsourcing vs hiring software developers main image

Outsourcing vs hiring software developers

When it comes to development of the next software project, you may start to consider how to get resources for it. There are three ways – software developers outsourcing, in-house hiring or using freelancers. In this post we will compare outsourcing and hiring software developers. Those options have much more sense in a long run than using freelancers, so we will limit this comparison to them. We’re going to explain the advantages and disadvantages of both approaches in deep.

Scythe-Studio - Chief Executive Officer

Łukasz Kosiński Chief Executive Officer

4 Best frameworks for cross-platform desktop app development main image

4 Best frameworks for cross-platform desktop app development

Have you ever noticed that most professional software and games are available for all the most popular platforms? This is normal, because when creating an application you want it to be available to as many customers as possible. A wider audience means more interest in your product, but also more potential profits you can get from it. So it is worth releasing your app on several, different platforms, but how to do it?

Scythe-Studio - Qt Developer

Mateusz Fibor Qt Developer

Qt Charts in Qt 6.2 main image

Qt Charts in Qt 6.2

The Qt Charts module provides the possibility to display various types of charts in order to visualize data. It is available in the Qt framework already for a quite long time, but the framework itself constantly evolves and it’s being improved all the time, so there were changes made to this framework as well. The newest, long-term support (LTS) version of the Qt framework is Qt 6.2 and this version includes most of the changes and improvements. Take a look at our blog post to learn more about improvements made to the Qt Charts in Qt 6.2.

Scythe-Studio - Chief Executive Officer

Łukasz Kosiński Chief Executive Officer