Enums in Qt QML

An enumerated type, or enum, is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type.

Enums are incredibly useful when portraying status, options, or modes that are exclusive within a grouping. A common enum that is seen in QML is the Status enum which is present in the Image, Loader, and may other elements. A common groupong of Status has the following values:

Null
Ready
Loading
Error

These enums can be used to track the status of an object. In the case of the Loader element, we can check the status of the file or component being loaded:

Loader {
    source: "SampleSource.qml"
    onStatusChanged: {
        if (status == Loader.Error) {
            console.warn("Error loading", source, sourceComponent.errorString());
        }
    }
}

Creating your own enums

Enums are easy to create in Qt. Simply use the C++ enum identifier and use the Q_ENUM macro to generate Qt-specific helper code.

I have found the best practice to exposing enums is to create a class for each enum you need. This way, each enum is not specifically associated to the class that is using it.

Let's create a new enum for Status similar to what we saw above. Create a new C++ class called StatusClass. We'll explain why we add the Class word at the end of the class name later.

statusclass.h

#pragma once

#include <QObject>

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

};

statusclass.cpp

#include "statusclass.h"

StatusClass::StatusClass()
{

}

By default, this class inherits QObject and has the Q_OBJECT macro to generate relevant MOC data for a QObject.

Since this is an enum class, we don't need to expansive feature set of the Q_OBJECT macro. Let's use Q_GADGET instead. Note that using Q_GADGET doesn't allow us to emit signals; but since this is an enum class we don't expect to emit any signals.

A Q_GADGET version of the class looks like this:

statusclass.h

#pragma once

#include <QObject>

class StatusClass
{
    Q_GADGET
public:
    explicit StatusClass();
};

Now, let's add our C++ enum:

statusclass.h

#pragma once

#include <QObject>

class StatusClass
{
    Q_GADGET
public:
    explicit StatusClass();

    enum Value {
        Null,
        Ready,
        Loading,
        Error
    };
};

In order to generate the Qt-specific helpers for this enum, we need to use the Q_ENUM macro. Be careful not to confuse this with the older, deprecated Q_ENUMS macro. You can read about the differences on woboq.

statusclass.h

#pragma once

#include <QObject>

class StatusClass
{
    Q_GADGET
public:
    explicit StatusClass();

    enum Value {
        Null,
        Ready,
        Loading,
        Error
    };
    Q_ENUM(Value)
};

Great! Now, we're ready to use the Status enum in QML.

Let's register the StatusClass type in main.cpp. We use qmlRegisterUncreatableType as we do not want instances of the StatusClass to be created in QML as it is simply a wrapper to a C++ enum.

main.cpp

qmlRegisterUncreatableType<StatusClass>("qml.guide", 1, 0, "StatusClass", "Not creatable as it is an enum type");

The last argument is a simple reason as to why the type is uncreatable. A warning will be logged if a user tries to instantiate this class:

qrc:/main.qml:16 Not creatable as it is an enum type

Now you can reference these enum values in QML:

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2

import qml.guide 1.0

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

    Component.onCompleted: {
        console.log(StatusClass.Ready);
    }
}

You will see the integer 1 printed in your log.

Why did we use Class at the end of the enum name? Let's clarify.
We created a class called StatusClass that wraps an enum called Value.

Our goal was to create an enum that can be exposed to both Qt and QML. We protected the instantiation of StatusClass from QML by using qmlRegisterUncreatableType. To protect the instantiation of an element in C++, we need to make the constructor private.

statusclass.h

#pragma once

#include <QObject>

class StatusClass
{
    Q_GADGET
public:
    enum Value {
        Null,
        Ready,
        Loading,
        Error
    };
    Q_ENUM(Value)

private:
    explicit StatusClass();
};

To use this class in C++, we would have to reference the class name and the Value enum name, e.g. void setStatus(StatusClass::Value status)

This requires us to reference Value to get the actual enum type. We can avoid this by using typedef to expose StatusClass::Value as Status:

typedef StatusClass::Value Status;

Now, our method signature can be void setStatus(Status status).
Much cleaner, isn't it?

For this typedef to work properly with QML, we must register both StatusClass and Status. This ensures the "Status" type is registered to the metatype system which allows us to assign a type of StatusClass to a type of Status via QML.

qRegisterMetaType<Status>("Status");
qmlRegisterUncreatableType<StatusClass>("qml.guide", 1, 0, "Status", "Not creatable as it is an enum type");

Voila! You have exposed an enum from Qt to QML, and also protected the enum for proper use in C++.

See the examples above in the qml.guide GitHub repository.

Niraj Desai

Niraj Desai

UI enthusiast, Qt / QML loyalist, passion for automotive UX – previously at Mercedes-Benz R&D, currently at NIO US.

Read More