如何从QML访问C++枚举?

56
class StyleClass : public QObject {
public:
    typedef enum
        {
            STYLE_RADIAL,
            STYLE_ENVELOPE,
            STYLE_FILLED
        }  Style;

    Style m_style;
    //...
};

上述代码在.h文件中。如何通过QML访问上述枚举?

9个回答

63
你可以将枚举包装在从QObject派生的类中(并将其公开给QML):
style.hpp:
#ifndef STYLE_HPP
#define STYLE_HPP

#include <QtGlobal>
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
    // Qt 4
    #include <QDeclarativeEngine>
#else
    // Qt 5
    #include <QQmlEngine>
#endif

// Required derivation from QObject
class StyleClass : public QObject
{
    Q_OBJECT

    public:
        // Default constructor, required for classes you expose to QML.
        StyleClass() : QObject() {}

        enum EnStyle
        {
            STYLE_RADIAL,
            STYLE_ENVELOPE,
            STYLE_FILLED
        };
        Q_ENUMS(EnStyle)

        // Do not forget to declare your class to the QML system.
        static void declareQML() {
            qmlRegisterType<StyleClass>("MyQMLEnums", 13, 37, "Style");
        }
};

#endif    // STYLE_HPP

主函数.cpp:

#include <QApplication>
#include "style.hpp"

int main (int argc, char ** argv) {
    QApplication a(argc, argv);

    //...

    StyleClass::declareQML();

    //...

    return a.exec();
}

QML 代码:

import MyQMLEnums 13.37
import QtQuick 2.0    // Or 1.1 depending on your Qt version

Item {
    id: myitem

    //...

    property int item_style: Style.STYLE_RADIAL

    //...
}

4
想知道是否有一种方法可以在不使用继承自QObject的类(例如在UI下面的软件层中,没有Qt依赖)中使用枚举。 - DavidJ
1
注意:已注册的类必须提供默认构造函数。 - Fabien
1
@DavidJ 我们也在想同样的问题。据我所知,你不能这样做。现在我们正在这样做:enum MyEnum { Foo = MyOtherEnum::Foo }; Q_ENUMS(MyEnum) - Alex Vallejo
2
@DavidJ 从Qt v5.8开始,您可以在命名空间中为枚举执行此操作。请参见下面的答案 - Maxim Paperno
1
@MaximPaperno 如果您无法修改头文件中的命名空间枚举,您会如何处理?我尝试声明一个新的命名空间并使用typedef注册枚举,例如:typedef OtherEnumNamespace::OtherEnum NewEnum; Q_ENUM_NS(NewEnum),但它不起作用。 - slayer
显示剩余5条评论

55

从Qt5.8开始,您可以从一个namespace中公开枚举:

定义命名空间和枚举:

#include <QObject>

namespace MyNamespace
{
    Q_NAMESPACE         // required for meta object creation
    enum EnStyle {
        STYLE_RADIAL,
        STYLE_ENVELOPE,
        STYLE_FILLED
    };
    Q_ENUM_NS(EnStyle)  // register the enum in meta object data
}

在创建Qml视图/上下文之前,注册命名空间(例如在main()函数中):

qmlRegisterUncreatableMetaObject(
  MyNamespace::staticMetaObject, // meta object created by Q_NAMESPACE macro
  "my.namespace",                // import statement (can be any string)
  1, 0,                          // major and minor version of the import
  "MyNamespace",                 // name in QML (does not have to match C++ name)
  "Error: only enums"            // error in case someone tries to create a MyNamespace object
);

在QML文件中使用它:

import QtQuick 2.0
import my.namespace 1.0

Item {
    Component.onCompleted: console.log(MyNamespace.STYLE_RADIAL)
}

参考文献:

https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/

http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterUncreatableMetaObject

http://doc.qt.io/qt-5/qobject.html#Q_ENUM_NS


2
如果你和我一样有疑问,staticMetaObject是通过 Q_NAMESPACE 宏添加到你的命名空间中的一个对象。 - pooya13
请记得在添加 Q_NAMESPACE 后重新运行 qmake,否则构建过程将无法找到 staticMetaObject。 - Matteo
这个解决方案是可行的,但是IDE(Qt Creator)有没有支持这个功能的方法呢?就是说 intelisense 能否在 QML 端获取枚举值?因为我发现现在你需要手动输入枚举名称。 - Curtwagner1984
似乎是QtCreator的一个未解决的bug https://bugreports.qt.io/browse/QTCREATORBUG-20569 - Mike

29

额外信息(Qt 5.5之前未记录):

您的枚举值名称必须以大写字母开头。

这样做是可行的:

enum EnStyle
{
    STYLE_RADIAL,
    STYLE_ENVELOPE,
    STYLE_FILLED
};
Q_ENUMS(EnStyle)

这不会:

enum EnStyle
{
    styleRADIAL,
    styleENVELOPE,
    styleFILLED
};
Q_ENUMS(EnStyle)

在编译时,您不会收到任何错误提示,它们只是被QML引擎忽略掉了。


8
我会尽力为您翻译这段话:@big_gie,我向尊敬的绅士提及“发布时”,以及当时我提交的Qt错误报告。现在已经有记录了,因为我采取了一些措施将其添加到文档中。 - Richard1403832
感谢您向Qt报告此问题!由于您的反馈,文档现在变得更加完善了 ;) 我只是想要添加(并引用确切位置),以便说明它已经被记录在文档中了。 - big_gie
1
几年前,我花了大部分时间来弄清楚为什么我的枚举在QML中没有显示出来,原因是... - ScottG

14

我在这里找到了一个非常好的解决方案,可以在QML中使用C++类中的ENUM,链接如下:Enums in Qt QML - qml.guide。该帖子非常好,我觉得有必要与SO社区分享,并且我认为应该始终进行归属声明,因此添加了帖子的链接。

该帖子基本上描述了:

1)如何在Qt/C++中创建ENUM类型:

// statusclass.h

#include <QObject>

class StatusClass
{
    Q_GADGET
public:
    explicit StatusClass();

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

2)如何将类注册到QML引擎作为“不可创建类型”:
(这是使此解决方案美观且独特的部分。)

// main.cpp

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

使用qmlRegisterUncreatableType可以防止在QML中实例化StatusClass。如果用户尝试实例化这个类,将会记录警告:

使用qmlRegisterUncreatableType可以防止在QML中实例化StatusClass。如果用户尝试实例化这个类,将会记录警告:

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

3) 最后,如何在 QML 文件中使用 ENUM:

// 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); // <--- Here's how to use the ENUM.
    }
}

重要提示:
应该使用类名引用ENUM,格式如下:StatusClass.Ready。如果与同一类在QML中作为上下文属性使用…

// main.cpp

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

StatusClass statusClassObj; // Named such (perhaps poorly) for the sake of clarity in the example.
engine.rootContext()->setContextProperty("statusClassObj", &statusClassObj); // <--- like this
...

...然后,有时人们会意外地在ENUM中使用上下文属性而不是类名。

// main.qml

...
Component.onCompleted: {
    // Correct
    console.log(StatusClass.Ready);    // 1

    // Wrong
    console.log(statusClassObj.Ready); // undefined
}
...
人们容易犯这个错误的原因是因为Qt Creator的自动完成功能在使用类名和上下文属性引用时都将ENUM列为选项。所以在这种情况下要小心谨慎。

8

自Qt 5.10起,Qt还支持使用QML定义枚举类型。除了air-dex提供的基于C ++的答案之外,现在你也可以使用QML创建枚举类型:

Style.qml:

import QtQuick 2.0

QtObject {
  enum EnStyle {
    STYLE_RADIAL,
    STYLE_ENVELOPE,
    STYLE_FILLED
  }
}

如果您只想在 QML 代码中使用这些枚举,那么这个解决方案会简单得多。您可以通过 qml 中的 Style 类型访问上述枚举,例如:

import VPlayApps 1.0
import QtQuick 2.9

App {

  property int enStyle: Style.EnStyle.STYLE_RADIAL

  Component.onCompleted: {
    if(enStyle === Style.EnStyle.STYLE_ENVELOPE)
      console.log("ENVELOPE")
    else
      console.log("NOT ENVELOPE")
  }
}

点击这里,可以看到基于QML的枚举类型的另一个使用示例。


3
有没有办法在C++中使用这些枚举? - jhasse

7

所有这些解决方案都不能将此枚举类用作信号/槽参数。这段代码可以编译,但在QML中无法工作:

class DataEmitter : public QObject
{
    Q_OBJECT

public:
    ...
signals:
    void setStyle(StyleClass::EnStyle style);
}

...

emit setStyle(StyleClass.STYLE_RADIAL);

QML部分:

Connections {
    target: dataEmitter
    onSetStyle: {
         myObject.style=style
    }
}

这段代码会产生运行时错误,如下所示:

IndicatorArea.qml:124: Error: Cannot assign [undefined] to int

为了让这段代码正常工作,您需要额外注册Qt元对象类型:
qRegisterMetaType<StyleClass::EnStyle>("StyleClass.EnStyle");

更多详细信息请参见此处:https://webhamster.ru/mytetrashare/index/mtb0/1535044840rbtgvfmjys(俄语)

1
现在这个也似乎在这里有记录:https://doc.qt.io/qt-5/qtqml-cppintegration-data.html#enumeration-types-as-signal-and-method-parameters - Marc Van Daele

3
使用Q_ENUMS宏将你的枚举类型注册到moc中,具体步骤见文档。在使用之前,必须按照文档所述,先注册“拥有”该枚举的类。
如果该枚举是全局的或由非QObject派生类所拥有,则Ashif的引用块才是有效的。

2
对于Qt 6.2及以上版本,在类中添加宏QML_ELEMENTQ_ENUM()(不是已弃用的Q_ENUMS())。由于该类是QObject,所以应该已经具有Q_OBJECT,以便在Qt MOC系统中进行注册。
例如:
class StyleClass : public QObject {
    Q_OBJECT       // Let the MOC know about this QObject
    QML_ELEMENT    // Make this object available to QML
public:
    typedef enum
        {
            STYLE_RADIAL,
            STYLE_ENVELOPE,
            STYLE_FILLED
        }  Style;

    Style m_style;
    Q_ENUM(Style)  // Make this enum available to QML
    //...
};

Qt的创新意味着这些宏已经足够了,不再需要手动注册类或枚举。
在QML中,您可以通过{ClassName}.{EnumValue}来访问枚举,而无需引用枚举名称。例如:StyleClass.STYLE_RADIAL

0

我的变量(为了向后兼容,当命名空间不可用时,请使用手动预定义的宏 ENABLE_USE_ENUM_NAMESPACES):

// enum.h
#pragma once

#include <QObject>

#ifndef ENABLE_USE_ENUM_NAMESPACES
class TaskTypeEnums : public QObject
{
    Q_OBJECT
    Q_ENUMS(TaskTypeEnum)

 public:
    explicit TaskTypeEnums(QObject *parent = nullptr): QObject(parent) {}
#else
namespace TaskTypeEnums {
    Q_NAMESPACE
#endif
    enum TaskTypeEnum {
        TaskConnectDev,
        TaskDisconnectDev,
        TaskPingDev,
        TaskResetDev,
        TaskTakeControlDev,
        TaskSetAkaModDev,
        TaskFirmwareUpdDev,
        TaskConnectDevs,
        TaskDisconnectAll,
        TaskMainTestSot,
        TaskMainTestBA,
    };
#ifndef ENABLE_USE_ENUM_NAMESPACES
};
#else
    Q_ENUM_NS(TaskTypeEnum)
}
#endif
// ----------------------------------------------------------------------------

// fragment from main.cpp
//...
#ifdef ENABLE_USE_ENUM_NAMESPACES
    qmlRegisterUncreatableMetaObject(TaskTypeEnums::staticMetaObject, "Vip.Enums.Tasks", 1, 0, "Tasks", "");
#else
    qmlRegisterType<TaskTypeEnums>("Vip.Enums.Tasks", 1, 0, "Tasks");
#endif
//...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接