一、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