目录
- 1. Q_ENUM宏 与 QMetaEnum类
- 1.1 Q_ENUM宏的作用
- 1.2 使用Q_ENUM注意的问题
- 1.3 在写有关枚举的代码时,我们可能遇到这种情况:需要用到枚举的字符串,该怎么办?
- 1.4 下面通过一段简单的代码来说明Q_ENUM的作用
- 2. Q_FLAG宏
- 2.1 Q_FLAG宏的作用
- 2.2 Q_DECLARE_FLAGS()宏的作用
- 2.3 Q_DECLARE_OPERATORS_FOR_FLAGS()宏的作用
- 2.4 演示代码
1. Q_ENUM宏 与 QMetaEnum类
1.1 Q_ENUM宏的作用
- 宏Q_ENUM会向元对象系统注册一个枚举类型
1.2 使用Q_ENUM注意的问题
- 使用Q_ENUM之前,必须在类中先声明Q_OBJECT或Q_GADGET宏。
- Q_ENUM(枚举类型)必须放在枚举声明之后,放在前面编译器会报错。
- Q_ENUM宏引入自Qt5.5版本,之前版本的Qt请使用Q_ENUMS宏,但Q_ENUMS宏不支持QMetaEnum::fromType()函数(这也是Q_ENUMS被弃用的原因)
1.3 在写有关枚举的代码时,我们可能遇到这种情况:需要用到枚举的字符串,该怎么办?
这时就需要用到 Q_ENUM宏 和 QMetaEnum类。
用Q_ENUM声明的枚举,其QMetaEnum注册在外部的QMetaObject中。在5.5版本之前可以使用QMetaObject获取QMetaEnum。
//before Qt5.5
QMetaObject object = MyEnum::staticMetaObject; //MyEnum是当前类,staticMetaObject返回类的元对象
int index = object.indexOfEnumerator("Orientation"); //Orientation是枚举的类型
QMetaEnum metaEnum = object.enumerator(index);
也可以使用静态函数QMetaEnum::fromType()来获取QMetaEnum。
QMetaEnum metaEnum = QMetaEnum::fromType<MyEnum::Orientation>(); //MyEnum是当前类,Orientation是枚举的类型
通过QMetaEnum对象就可以获得枚举的一些信息以及枚举类型与字符串类型的转换。
1.4 下面通过一段简单的代码来说明Q_ENUM的作用
#include <QDebug>
#include <QDialog>
#include <QMetaEnum>
class MyEnum : public QDialog {
Q_OBJECT
public:
enum Orientation {
Up = 1,
Down = 2,
Left = 3,
Right = 4
};
Q_ENUM(Orientation) //向元对象系统注册枚举类型
MyEnum(QWidget* parent = 0);
~MyEnum();
};
#include "myenum.h"
MyEnum::MyEnum(QWidget* parent) :
QDialog(parent) {
//获取QMetaEnum对象的方法1:
//QMetaObject object = MyEnum::staticMetaObject; //before Qt5.5
//int index = object.indexOfEnumerator("Orientation"); //Orientation是枚举的类型
//QMetaEnum metaEnum = object.enumerator(index);
//获取QMetaEnum对象的方法2:
QMetaEnum metaEnum = QMetaEnum::fromType<MyEnum::Orientation>(); //通过静态函数fromType获取QMetaEnum对象
//QMetaEnum会对所有的枚举进行下标编号,从0开始
QString name = metaEnum.name(); //枚举名称
int count = metaEnum.keyCount(); //枚举数量
QString keyIndex = metaEnum.key(0); //下标为0的key
int valueIndex = metaEnum.value(0); //下标为0的value
QString Key = metaEnum.valueToKey(MyEnum::Left); //通过value得到key
int value = metaEnum.keyToValue("Left"); //通过key得到value
qDebug() << "枚举的名称:" << name;
qDebug() << "枚举的数量:" << QString::number(count);
qDebug() << "index下标的key值:" << keyIndex;
qDebug() << "index下标的Value值:" << QString::number(valueIndex);
qDebug() << "value对应的key值:" << Key;
qDebug() << "key值对应的Vaule:" << QString::number(value);
}
MyEnum::~MyEnum() {}
需要知道的是:
QMetaEnum会对所有的枚举进行下标编号,从0开始。
QMetaEnum对枚举的保存是通过key和value的方式进行保存和获取的。
以上代码可以看出Q_ENUM声明的枚举,通过QMetaEnum对象可以获得枚举的许多信息。例如枚举的名称、枚举的数量、枚举的key和value值等等。
2. Q_FLAG宏
2.1 Q_FLAG宏的作用
宏Q_FLAG会向元对象系统注册一个单一的标志类型。
使用宏Q_FLAG声明的枚举,其枚举值可以作为标志,并使用位或操作符(|)进行组合。
如果进行位或操作,那上面的代码中枚举值就不能那样定义,因为位或操作是按照二进制位进行“或运算”,其枚举值的定义需要按照位来定义(即不同的枚举值其二进制的每一位都不同)。例如:
enum Orientation {
Up = 0x01, //即0000...0001
Down = 0x02, //即0000...0010
Left = 0x04, //即0000...0100
Right = 0x08 //即0000...1000
};
2.2 Q_DECLARE_FLAGS()宏的作用
Q_DECLARE_FLAGS(Flags, Enum)宏展开为:
typedef QFlags<Enum> Flags;
QFlags<Enum>是一个模板类,其中Enum是枚举类型,QFlags用于存储枚举值的组合。
传统的 C++ 编程中,通常使用整数来保存 enum 的逻辑运算结果 (与、或、非、异或等),在进行逻辑运算的时候没有进行类型检查,一个枚举类型可以和其他的枚举类型进行逻辑运算,运算的结果可以直接传递给接收参数为整数的函数。
下面看一个例子:
enum Orientation
{
Up = 1, //即0000...0001
Down = 2, //即0000...0010
Left = 4, //即0000...0100
Right = 8 //即0000...1000
};
enum Direction
{
horizontal = 1,
vertical = 2
};
这种操作编译器不会报错:
Orientation::Up | Direction::horizontal; //两个不相关的枚举值做逻辑运算没有意义
对于上面的问题,应该怎么解决?
Qt 中,模板类 QFlags<Enum> 提供了类型安全的方式保存 enum 的逻辑运算结果,来解决上面的问题。
这种方式在 Qt 里很常见,例如设置 QLabel 对齐方式的函数是 QLabel::setAlignment(Qt::Alignment) (typedef QFlagsQt::AlignmentFlag Qt::Alignment),这就意味着传给 setAlignment 的参数只能是枚举 Qt::AlignmentFlag 的变量、它们的逻辑运算结果或者 0,如果传入其他的枚举类型或者非 0 值,编译时就会报错,例如:
label->setAlignment(0); // OK
label->setAlignment(Qt::AlignLeft | Qt::AlignTop); // OK
label->setAlignment(Qt::WA_Hover); // Error: 编译时报错
总之,Q_DECLARE_FLAGS(Flags, Enum)宏将普通结构体Enum重新定义成了一个可以自由进行位或操作的安全的结构体Flags。
2.3 Q_DECLARE_OPERATORS_FOR_FLAGS()宏的作用
- Q_DECLARE_OPERATORS_FOR_FLAGS(Flags)赋予了Flags一个全局操作符“|”,没有这个宏语句,Flags量之间进行与操作后的结果将是一个int值,而不是Flags值。这点特别重要。
- Q_DECLARE_OPERATORS_FOR_FLAGS必须定义在类外。
- Q_DECLARE_OPERATORS_FOR_FLAGS只提供了“或”操作,没有提供“与”“非”操作。
- Q_DECLARE_FLAGS和Q_DECLARE_OPERATORS_FOR_FLAGS都是和元对象系统无关的,可以脱离Q_FLAG单独使用,事实上这两个宏在Qt4就已经存在(不确定更早是否存在),而Q_FLAG是在Qt5.5版本才加入的。
2.4 演示代码
#include <QDebug>
#include <QDialog>
#include <QMetaEnum>
#include <QMetaObject>
class MyEnum : public QDialog {
Q_OBJECT
public:
enum Orientation {
Up = 0x01, //即0000...0001
Down = 0x02, //即0000...0010
Left = 0x04, //即0000...0100
Right = 0x08 //即0000...1000
};
Q_ENUM(Orientation)
Q_DECLARE_FLAGS(Orientations, Orientation)
Q_FLAG(Orientations)
MyEnum(QWidget* parent = 0);
~MyEnum();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(MyEnum::Orientations)
#include "myenum.h"
MyEnum::MyEnum(QWidget* parent) :
QDialog(parent) {
QMetaEnum metaEnum = QMetaEnum::fromType<MyEnum::Orientation>(); //通过静态函数fromType获取QMetaEnum对象
QString name = metaEnum.name(); //枚举名称
int count = metaEnum.keyCount(); //枚举数量
QString keyIndex = metaEnum.key(0); //下标为0的key
int valueIndex = metaEnum.value(0); //下标为0的value
QString Key = metaEnum.valueToKeys(MyEnum::Left | MyEnum::Right); //通过value得到key
int value = metaEnum.keysToValue("Up | Down"); //通过key得到value
qDebug() << "枚举的名称:" << name;
qDebug() << "枚举的数量:" << QString::number(count);
qDebug() << "index下标的key值:" << keyIndex;
qDebug() << "index下标的Value值:" << QString::number(valueIndex);
qDebug() << "value对应的key值:" << Key;
qDebug() << "key值对应的Vaule:" << QString::number(value);
}
MyEnum::~MyEnum() {}