【问题标题】:How to access C++ enum from QML?如何从 QML 访问 C++ 枚举?
【发布时间】:2013-12-04 01:08:27
【问题描述】:
class StyleClass : public QObject {
public:
    typedef enum
        {
            STYLE_RADIAL,
            STYLE_ENVELOPE,
            STYLE_FILLED
        }  Style;

    Style m_style;
    //...
};

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

【问题讨论】:

    标签: c++ qt qml qtquick2


    【解决方案1】:

    我的变体(为了在命名空间不可用时向后兼容,请使用手动预定义宏 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
    //...
    

    【讨论】:

      【解决方案2】:

      从 Qt 5.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
      }
      

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

      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

      【讨论】:

      • 如果您像我一样想知道,staticMetaObject 是由Q_NAMESPACE 宏添加到您的命名空间的对象。
      • 添加Q_NAMESPACE后记得重新运行qmake,否则构建过程找不到staticMetaObject
      【解决方案3】:

      我在这里找到了一个非常好的解决方案,可以在 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。如果用户尝试实例化此类,将记录警告:

      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 列为选项,无论是在使用类名引用时还是使用上下文属性。因此,在这种情况下请谨慎行事。

      【讨论】:

        【解决方案4】:

        所有这些解决方案都不能使用这个枚举类作为信号/插槽的参数。这段代码可以编译,但不能在 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 (rus)

        【讨论】:

        【解决方案5】:

        Qt 从 Qt 5.10 版开始也支持 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 的枚举类型的另一个使用示例,请参阅 here

        【讨论】:

        • 有什么方法可以在 C++ 中使用这些枚举?
        【解决方案6】:

        其他信息(Qt 5.5 之前未记录):

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

        这将起作用:

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

        这不是:

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

        在编译时不会出现任何错误,它们只是被 QML 引擎忽略。

        【讨论】:

        • @big_gie 我请尊敬的先生参考“发布时”这句话,以及我当时所做的 Qt 错误报告。它现在已记录在案因为我采取了一些措施将其添加到文档中。
        • 感谢您向 Qt 报告!多亏了您,文档现在状态更好了;)我只是想添加(并参考确切位置)它现在已记录在案。
        • 几年前,我花了一天的大部分时间试图弄清楚为什么我的枚举没有出现在 QML 中...
        【解决方案7】:

        您可以将枚举包装在一个派生自 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
        

        main.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
        
            //...
        }
        

        【讨论】:

        • 想知道是否有办法让不在 QObject 派生类中的枚举(例如在没有 Qt 依赖的 UI 下方的软件层中)。
        • 注意:注册的类必须提供默认构造函数
        • @DavidJ 我们想知道同样的事情。据我所知,你不能这样做。现在我们正在这样做:enum MyEnum { Foo = MyOtherEnum::Foo }; Q_ENUMS(MyEnum)
        • @DavidJ 从 Qt v5.8 开始,您可以为命名空间中的枚举执行此操作。见answer below
        • @MaximPaperno 您将如何使用您无法修改的头文件中的命名空间枚举来执行此操作?我试图声明一个新的命名空间并使用 typedef 注册枚举,例如: typedef OtherEnumNamespace::OtherEnum NewEnum; Q_ENUM_NS(NewEnum) 但是不行
        【解决方案8】:

        使用Q_ENUMS 宏使moc 了解您的枚举,如docs 中所述。您必须在使用枚举之前注册“拥有”枚举的类,如docs 中所述。

        Ashif 的引用块仅在枚举是全局的或由非QObject 派生类拥有时才有效。

        【讨论】:

          猜你喜欢
          • 2018-01-09
          • 2021-05-21
          • 1970-01-01
          • 2011-10-05
          • 1970-01-01
          • 2018-01-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多