Qt中的枚举变量,Q 您所在的位置:网站首页 结构体数组做参数传递的方法 Qt中的枚举变量,Q

Qt中的枚举变量,Q

2024-07-01 17:08| 来源: 网络整理| 查看: 265

Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他 理论基础:一、Q_ENUM二、QMetaEnum三、Q_FLAG四、示例 Chapter1 Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他前言Q_ENUM的使用Q_FLAG的引入解决什么问题?Q_NAMESPACE,Q_ENUM_NS和Q_FLAG_NS新旧对比 Chapter2 【QT】枚举常用宏Chapter3 Qt中自定义结构体、枚举型做信号参数传递参考链接问题解决示例为什么不使用Q_ENUMS()? Chapter4 如何在Qt信号和槽中使用枚举Chapter5 个人总结(2023-10)user.huser.cpp

理论基础: 一、Q_ENUM

用Q_ENUM,就可以将自定义枚举类型注册到Qt的元对象系统中,从而实现枚举到各种类型的转换。

既然是元对象系统,就会使用Moc编译,因此,枚举类型需要放在继承于添加了Q_OBJECT宏,继承QObject的类中;并且声明为public属性,以便外部使用。

二、QMetaEnum

QMetaEnum实现了字符串到枚举,枚举到字符串的转换,以及一些其他的转换,这样可以增加代码的可读性。具体大家可以在帮助手册中看看QMetaEnum的接口。

其他类转换到QMetaEnum 类型可以使用QMetaEnum::fromType()获取QMetaEnum对象。

三、Q_FLAG

Q_ENUM可以实现大部分常用功能,引入Q_FLAG主要为了解决枚举变量的组合使用,增加枚举变量间与或非计算,比如Up是1,Left是4,则Up|Left是5,为一个有意义的值。

四、示例 #ifndef USER_H #define USER_H #include class User : public QObject { Q_OBJECT public: explicit User(QObject *parent = nullptr); ~User(); enum Authorization { //增加用户权限枚举类型,USER表示操作员,ADMIN表示管理员,SUPPER_ADMIN表示超级管理员 USER = 1, ADMIN = 2, SUPPER_ADMIN =3 }; Q_ENUM(Authorization) Q_DECLARE_FLAGS(AuthorizationFlags, Authorization) Q_FLAG(AuthorizationFlags) public: static User* getHandle(); User::Authorization getAuthorization(); //获取当前用户权限 signals: private: static User* handle; User::Authorization authorization; //用户权限 }; Q_DECLARE_OPERATORS_FOR_FLAGS(User::AuthorizationFlags) #endif // USER_H Chapter1 Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他

原文链接:https://blog.csdn.net/qq_36179504/article/details/100895133

前言

之前做一个比较大工程,核心数据里面有很多是枚举变量,需要频繁地使用枚举量到字符串和字符串到枚举量的操作,为了实现这些操作,我把每个枚举类型后面都附加了两个类似Enum_to_String()和String_to_Enum()的函数,程序显得很臃肿。这时候就非常羡慕C#或者java等兄弟语言,内核内置了枚举量和字符串转换的方法。 最近读Qt文档时偶然间发现,Qt内核其实已经提供了这个转换机制,使得我们能用很少的代码完成枚举量和字符串的转换,甚至还能实现其他更酷更强大的功能,下面我们就来看看如何使用Qt的这个功能。 简单来讲,Qt还是使用一组宏命令来完成枚举量扩展功能的(正如Qt的其他核心机制一样),这些宏包括Q_ENUM,Q_FLAG,Q_ENUM_NS,Q_FLAG_NS,Q_DECLARE_FLAGS,Q_DECLARE_OPERATORS_FOR_FLAGS, 这些宏的实现原理和如何展开如何注册到Qt内核均不在本文的讲解范围,本文只讲应用。

Q_ENUM的使用

首先讲解最简单明了的宏Q_ENUM,先看代码:

#include class MyEnum : public QObject { Q_OBJECT public: explicit MyEnum(QObject *parent = nullptr); enum Priority { High = 1, Low = 2, VeryHigh = 3, VeryLow = 4 }; Q_ENUM(Priority) };

这就是在类中定义了一个普通的枚举变量之后,额外加入了Q_ENUM(枚举类型名)这样的一个宏语句,那么加入了这个Qt引入的宏语句后,我们能得到什么收益呢?

qDebug() Up = 1, Down = 2, Left = 4, Right = 8 };

注意这里枚举值被定义成等比数列,这个技巧给使用"|“操作符扩展留下了空间,比如,左上可以用Up | Left来简单表示,但是这里也带来了一个问题,Up | Left值是一个整型,并不在枚举结构Orientation中,如果函数使用Orientation作为自变量,编译器是无法通过的,为此往往把函数自变量类型改为整型,但因此也就丢掉了类型检查,输入量有可能是其他无意义的整型量,在运行时带来错误。

Qt引入QFlags类,配合一组宏命令完美地解决了这个问题。

QFlags是一个简单的类,可以装入一个枚举量,并重载了与或非等运算符,使得枚举量能进行与或非运算,且运算结果还是一个QFlags包装的枚举量。一个普通的枚举类型包装成QFlags型,需要使用Q_DECLARE_FLAGS宏,在全局任意地方使用”|"操作符计算自定义的枚举量,需要使用Q_DECLARE_OPERATORS_FOR_FLAGS宏。 再看一段代码:

class MyEnum : public QObject { Q_OBJECT public: explicit MyEnum(QObject *parent = nullptr); enum Orientation { Up = 1, Down = 2, Left = 4, Right = 8, }; Q_ENUM(Orientation) //如不使用Orientation,可省略 Q_DECLARE_FLAGS(OrientationFlags, Orientation) Q_FLAG(OrientationFlags) }; Q_DECLARE_OPERATORS_FOR_FLAGS(MyEnum::OrientationFlags)

上面这段代码展示了使用Q_FLAG包装枚举定义的方法,代码中Q_DECLARE_FLAGS(Flags, Enum)实际上被展开成typedef QFlags< Enum > Flags,所以Q_DECLARE_FLAGS实际上是QFlags的定义式,之后才能使用Q_FLAG(Flags)把定义的Flags注册到元对象系统。Q_FLAG完成的功能和Q_ENUM是类似的,使得枚举量可以被QMetaEnum::fromType()调用。 看一下使用代码:

qDebug() Up = 0x01, //即0000...0001 Down = 0x02, //即0000...0010 Left = 0x04, //即0000...0100 Right = 0x08 //即0000...1000 };

1.2 Q_DECLARE_FLAGS()宏的作用 Q_DECLARE_FLAGS(Flags, Enum)宏展开为:

typedef QFlags Flags;

QFlags是一个模板类,其中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 的逻辑运算结果,来解决上面的问题。

这种方式在 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。

1.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版本才加入的。 1.4 演示代码

#include #include #include #include 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(); //通过静态函数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() Q_OBJECT public: enum COMMUNICATION_METHOD { TCP_CONNECT = 0, UDP_CONNECT, COM_CONNECT }; enum CONNECT_TYPE { LONG_CONNECTION = 0, SHORT_TCONNECTION }; Q_ENUM(COMMUNICATION_METHOD) Q_ENUM(CONNECT_TYPE) explicit Communication(QObject *parent = NULL); ~Communication(); }

Communication.cpp如下:

Communication::Communication(QObject *parent) : QObject(parent) { qRegisterMetaType("Communication::CONNECTTYPE"); qRegisterMetaType("QAbstractSocket::SocketState"); }

补充

为什么不使用Q_ENUMS()?

关于Q_ENUMS(),Qt5.14.2的Qt助手是这样描述的:

This function is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code. This macro registers one or several enum types to the meta-object system. If you want to register an enum that is declared in another class, the enum must be fully qualified with the name of the class defining it. In addition, the class defining the enum has to inherit QObject as well as declare the enum using Q_ENUMS(). In new code, you should prefer the use of the Q_ENUM() macro, which makes the type available also to the meta type system. For instance, QMetaEnum::fromType() will not work with types declared with Q_ENUMS().

大意可翻译为:

此功能已过时。 提供它是为了使旧的源代码正常工作。 我们强烈建议不要在新代码中使用它。 此宏将一种或几种枚举类型注册到元对象系统。 如果要注册在另一个类中声明的枚举,则该枚举必须使用定义它的类的名称完全限定。 另外,定义枚举的类必须继承QObject并使用Q_ENUMS()声明枚举。 在新代码中,您应该更喜欢使用Q_ENUM()宏,这会使该类型也可用于元类型系统。 例如,QMetaEnum :: fromType()不适用于用Q_ENUMS()声明的类型。 Chapter4 如何在Qt信号和槽中使用枚举

我在信号中使用enum类型时遇到了一些问题。基本上我有两个类,一个状态机和一个处理状态机的线程。当状态改变时,我想发送一个带有新状态的信号。我还希望使用enum来表示状态。在我的完整代码中,状态机是在一个单独的共享库中实现的,但是下面的代码给出了完全相同的错误。

当我运行代码时,我得到了以下行为:

kotte@EMO-Ubuntu:sigenum $ ./sigenum Object::connect: No such slot MyThread::onNewState(state) Test signal Test signal

我在我的示例代码四个文件:statemachine.h,statemachine.cpp,main.h和main.cpp.main函数只是启动线程,然后线程创建一个实例StateMachine并处理来自的线程StateMachine.我对Qt很陌生,所以当我意识到你必须将enum包含在内Q_ENUMS并用类型系统注册它时,我有点困惑.所以我完全有可能犯了一些菜鸟错误

下面的代码有点长,但我希望它与我的真实代码尽可能相似.

statemachine.h 好像:

// statemachine.h #ifndef _STATEMACHINE_H #define _STATEMACHINE_H #include class StateMachine : public QObject { Q_OBJECT Q_ENUMS(state) public: enum state {S0, S1, S2}; void setState(state newState); signals: void stateChanged(state newState); void testSignal(void); }; Q_DECLARE_METATYPE(StateMachine::state); #endif

并且它被实现为:

// statemachine.cpp #include #include "statemachine.h" void StateMachine::setState(state newState) { emit stateChanged(newState); emit testSignal(); }

该线程被定义为

// main.h #ifndef _MAIN_H #define _MAIN_H #include #include "statemachine.h" class MyThread : public QThread { Q_OBJECT private: void run(void); private slots: void onNewState(StateMachine::state); void onTestSignal(void); private: StateMachine *myStateMachine; }; #endif

其实现方式如下:

// main.cpp #include #include #include "statemachine.h" #include "main.h" void MyThread::run() { myStateMachine = new StateMachine(); qRegisterMetaType("state"); // This does not work connect(myStateMachine, SIGNAL(stateChanged(state)), this, SLOT(onNewState(state))); // But this does... connect(myStateMachine, SIGNAL(testSignal()), this, SLOT(onTestSignal())); forever { // ... myStateMachine->setState(StateMachine::S0); } } void MyThread::onTestSignal() { qDebug() Q_OBJECT public: explicit User(QObject *parent = nullptr); ~User(); enum Authorization { //增加用户权限枚举类型,USER表示操作员,ADMIN表示管理员,SUPPER_ADMIN表示超级管理员 USER = 1, ADMIN = 2, SUPPER_ADMIN =3 }; Q_ENUM(Authorization) Q_DECLARE_FLAGS(AuthorizationFlags, Authorization) Q_FLAG(AuthorizationFlags) public: static User* getHandle(); void init(); User::Authorization getAuthorization(); //获取当前用户权限 void setAuthorization(User::Authorization authorization); signals: private: static User* handle; QString filePath; int level_Admin; int level_SuperAdmin; QString admin_Passwd; QString superAdmin_Passwd; User::Authorization m_authorization; //用户权限 }; Q_DECLARE_OPERATORS_FOR_FLAGS(User::AuthorizationFlags) Q_DECLARE_METATYPE(User::Authorization); #endif // USER_H user.cpp

注意:在类的构造函数中加入qRegisterMetaTypeUser::Authorization(“User::Authorization”); //向Qt元数据系统注册枚举类型,否则无法使用信号槽传递枚举参数;

#include "user.h" User* User::handle = nullptr; User::User(QObject *parent) : QObject(parent) { qRegisterMetaType("User::Authorization"); //向Qt元数据系统注册枚举类型,否则无法使用信号槽传递枚举参数 this->filePath = ConfigPath("user.json"); this->m_authorization = User::Authorization::USER; //默认是操作员权限 } User::~User() { } User *User::getHandle() { if(handle == nullptr) { handle = new User(); } return handle; } void User::init() { if(!FileExists(filePath)) { // create(); } // load(); } void User::create() { this->level_Admin = 1; this->level_SuperAdmin =2; this->admin_Passwd = "123456"; this->superAdmin_Passwd = "112233"; save(); } void User::load() { } void User::save() { } User::Authorization User::getAuthorization() { return this->m_authorization; } void User::setAuthorization(Authorization authorization) { this->m_authorization = authorization; } void LoginDlg::on_btn_Login_clicked() { if(ui->lineEdit->text().isEmpty()) //如果密码输入为空,则执行关闭命令 { this->on_btn_Close_clicked(); } else{ this->passwdInput = ui->lineEdit->text().toLatin1(); //获取用户密码 this->level = ui->comboBox->currentIndex(); //获取用户级别 if(this->passwdInput == tr("123456")) { User::getHandle()->setAuthorization(User::Authorization::ADMIN); } else if(this->passwdInput == tr("112233")) { User::getHandle()->setAuthorization(User::Authorization::SUPPER_ADMIN); } else{ User::getHandle()->setAuthorization(User::Authorization::USER); } ui->lineEdit->clear(); this->accept(); } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有