Search code examples
c++enumsqmlqt6

How to expose enum to QML from non QObject in C++ (Qt 6)


I want to expose an enum to QML from C++ without creating a QObject but it's not working for me. Any ideas?

For comparison, I've included minimal code exposing an enum to QML using Q_ENUM() from a QObject that contains Q_OBJECT and QML_ELEMENT.

I have tried using a class that contains Q_GADGET and uses Q_ENUM(). I have also tried using a namespace with Q_NAMESPACE() and Q_ENUM_NS(). In both cases the enum doesn't get recognised by QML.

Full QObject QML_ELEMENT example

#include <QObject>
#include <QtQml>

class MyFullQObject : public QObject {
  Q_OBJECT
  QML_ELEMENT
public:
  using QObject::QObject;

  enum MyEnum {
    First,
    Second,
    Third
  };
  Q_ENUM(MyEnum)
};

Q_GADGET example

#include <QtQml>

class MyGadget {
  Q_GADGET
public:
  enum MyEnum {
    First,
    Second,
    Third
  };
  Q_ENUM(MyEnum)
};

Q_NAMESPACE example

#include <QObject>

namespace MyNamespace {

Q_NAMESPACE

enum MyEnum {
  First,
  Second,
  Third
};
Q_ENUM_NS(MyEnum)

}

Usage in QML

import QtQuick
import MyModule

Item {
  Component.onCompleted: {
    console.log("MyFullQObject enum: " + MyFullQObject.First)
    console.log("MyGadget enum: " + MyGadget.First)
    console.log("MyNamespace enum: " + MyNamespace.First)
  }
}

In QML, the first console line will work. The second one generates an error "ReferenceError: MyGadget is not defined". Likewise for the third one (if the second one is commented out to allow the program to continue).

Within CMake, both classes and the namespace are included in the same CMake module and I link the generated MyModuleLibplugin to the executable, which successfully makes it available for QML import.


Solution

  • There is a simple solution that uses only macros without needing to manually register anything in main.cpp.

    Normally you would use QML_ELEMENT with Q_OBJECT as in MyFullQObject to cause a QObject to be made available to QML.

    However this same macro can be used with Q_GADGET or Q_NAMESPACE and this results in their enums becoming available in QML.

    --

    As a side note for curiosity, while it now becomes possible to put the following in QML (without error and with normal component highlighting in Qt Creator):

     MyGadget { id: myGadget
        Component.onCompleted: console.log('Made a MyGadget')
     }
    
     MyNamespace { id: myNamespace
        Component.onCompleted: console.log('Made a MyNamespace')
     }
    

    it would generate the runtime error "Element is not creatable", as you would expect.