QML(19)——枚举量enum的使用

一、C++注册枚举给Qml使用

1.cpp中定义枚举量

该类必须继承 Q_OBJECT
枚举的名称及枚举量的名称,必须是大驼峰!

// BasicTypeTest.cpp
#ifndef BASICTYPETEST_H
#define BASICTYPETEST_H
#include <QObject>

class BasicTypeTest : public QObject
{
    Q_OBJECT
public:
    enum MyEnum {
        Option1,   // 必须大驼峰!
        Option2,
        Option3
    };
    Q_ENUM(MyEnum)
    static BasicTypeTest* instance();
    explicit BasicTypeTest(QObject *parent = nullptr);

#endif // BASICTYPETEST_H

2. 主cpp中注册枚举量所在的类

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "BasicTypeTest.h"

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    // 注册 BasicTypeTest
    qmlRegisterType<BasicTypeTest>("MyLib", 1, 0, "ClassA");

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

3.qml中使用

import QtQuick 2.15
import MyLib 1.0

Rectangle{
    width: 50
    height: 50
    color: "grey"

    MouseArea {
        anchors.fill: parent
        onClicked: {
            var a = ClassA.Option1;
            console.log("a= ",a)
            console.log("ClassA.Option2 ",ClassA.Option2)
        }
    }
}

二、Qml中定义枚举

1.qml文件内部使用

自定义的qml文件必须首字母大写
枚举没有语法高亮,但是能正常用

// TestEnum.qml
import QtQuick 2.15
import MyLib 1.0

Rectangle{
    width: 50
    height: 5
    enum Fruit {
        Apple = 5,
        Banana,
        Orange
    }

    Component.onCompleted: {
        console.log(TestEnum.Fruit.Apple)
        console.log(TestEnum.Fruit.Banana)
        console.log(TestEnum.Fruit.Orange)
    }
}

2.外部qml文件调用枚举量

import QtQuick 2.15
Item {
    property int orange: TestEnum.Fruit.Orange
    Component.onCompleted: {
        console.log("orange", orange)
        console.log(TestEnum.Fruit.Apple)
        console.log(TestEnum.Fruit.Banana)
        console.log(TestEnum.Fruit.Orange)
    }
}

三、 枚举量和字符串转换

ScopedEnum a = ScopedEnum::ScopeValue01;
QString enumString =QVariant::fromValue(a).toString();
qDebug() << "a:" << enumString;

// output
a: "ScopeValue01"

四 、多个枚举量,需要作用域

直接使用enum{}, 在调用枚举时,“类名.枚举成员值”, 没有枚举量的名称。

class Message : public QObject
{
   Q_OBJECT
public:
    enum class ScopedEnum {
        ScopeValue01 = 0,
        ScopeValue02
    };
    Q_ENUM(ScopedEnum)
    enum class OtherValue {
        OtherValue01 = 10,
        OtherValue02
    };
    Q_ENUM(OtherValue)
};
import QtQuick 2.15
import MyLib 1.0

Rectangle{
    width: 50
    height: 50

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log(ClassA.ScopedEnum.ScopeValue01)
            console.log(ClassA.ScopedEnum.ScopeValue02)
            console.log(ClassA.OtherValue.OtherValue01)
            console.log(ClassA.OtherValue.OtherValue02)
        }
    }
}
// Output
qml: 0
qml: 1
qml: 10
qml: 11

如果出现枚举类中成员名冲突,可以通过使用 Q_CLASSINFO
来禁用无作用域注册。设置RegisterEnumClassesUnscoped 为false以防止作用域枚举合并到同一名称空间中。

class Message : public QObject
    {
        Q_OBJECT
        Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
        
    public:
        enum class ScopedEnum {
              Value1,
              Value2,
              OtherValue
        };
        Q_ENUM(ScopedEnum)
        
        enum class OtherValue {
              Value1,
              Value2
        };
        Q_ENUM(OtherValue)
    };
import QtQuick 2.15
import MyLib 1.0

Rectangle{
    width: 50
    height: 50

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log(ClassA.ScopedEnum.Value1)
            console.log(ClassA.ScopedEnum.Value2)
            console.log(ClassA.ScopedEnum.OtherValue)
            console.log(ClassA.OtherValue.Value1)
            console.log(ClassA.OtherValue.Value2)
        }
    }
}
// Output
qml: 0
qml: 1
qml: 2
qml: 10
qml: 11

五、 Enum 和 Enum class的区别

C++中有两种声明Enum的方式,分别是enum和enum class。
enum从古老的C++98里面就存在,而enum class从C++11开始进入大家视野。

1. 声明的枚举变量所影响的范围不同

enum class 又被叫做scoped enum。顾名思义,enum class中声明的枚举值不会造成变量污染,举个例子:

	enum enumFruit
	{
		APPLE = 0,
		BANANA = 1
	};
	int APPLE = 20; //redefination

这段代码没法通过编译,编译器会告诉你APPLE已经被定义过了,想要修正它就使用enum class

	enum class enumClassFruit
	{
		APPLE = 0,
		BANANA = 1
	};
	int APPLE = 20; //that is fine

推而广之,在使用枚举值的时候,enum class也需要加上enum名称

enumClassFruit f1 = enumClassFruit::APPLE; //that is fine
enumClassFruit f2 = APPLE; //that is wrong

2. 提供不同级别的类型安全

对enum而言,编译器提供比较弱的类型安全,形如下面的代码可以通过编译

if (APPLE < 10)
{
//some code here
}

但对于enum class,编译器不允许这样的代码

if (enumClassFruit::APPLE < 10) //wrong code
{
}

必须要用显示转型才行

if (static_cast<int>(enumClassFruit::APPLE) < 10) //that is fine
{
}

3. 有不同的默认底层数据结构

对于enum,并没有默认底层数据结构,C++标准规定让编译器选择实现,只要确保底层数据结构能装下最大的枚举值,对于

	enum enumFruit
	{
		APPLE = 0,
		BANANA = 1
	};

编译器可能在底层用short就可以装下这些枚举值,所以出于空间优化的考虑,编译器也许会用short,不过这确实是编译器自己决定。当然,如果之后enumFruit里面又添加一个值

	enum enumFruit
	{
		APPLE = 0,
		BANANA = 1,
		ORANGE = 0xffffffff
	};
	std::cout << enumFruit::ORANGE << std::endl; // -1

编译器就会使用 int 来充当底层数据结构,这也是为什么输出是-1。当然如果需要确保枚举值都是非负值,我们可以手动指定enum的底层数据结构

	enum enumFruit : unsigned int
	{
		APPLE = 0,
		BANANA = 1,
		ORANGE = 0xffffffff
	};
	std::cout << enumFruit::ORANGE << std::endl; // 4294967295

另一方面,对scoped enum,c++语言规定了默认底层数据结构是Int。所以在编译器看来,如下代码

	enum class enumClassFruit
	{
		APPLE = 0,
		BANANA = 1
	};

其实就等同于

	enum class enumClassFruit : int
	{
		APPLE = 0,
		BANANA = 1
	};

当然和enum一样,也可以根据实际需求定制底层数据结构。

4. 选择机制

一般来说,在可以使用scoped enum的时候尽量选择scoped enum,毕竟有如上所说的优点,但是在有些情况下也需要使用enum, 比如在类中模拟const static int的enum hack,就需要使用enum。

class TestClass
{
public:
	enum Constants
    {
        Value = 1
    };
}
auto i = TestClass::Value; //use it like static const

参考资料: C++中的Enum与Enum class

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值