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.
Subscribe to QML Guide
Get the latest posts delivered right to your inbox