Qt中的枚举变量,Q_ENUM,Q_FLAG以及Qt中自定义结构体、枚举型做信号参数传递

news2024/9/21 4:23:35

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.h
    • user.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 <QObject>

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 <QObject>

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()<< MyEnum::High<< MyEnum::Low;                  //qDebug()可以直接打印出枚举类值的字符串名称
QMetaEnum m = QMetaEnum::fromType<MyEnum::Priority>();  //since Qt5.5
qDebug()<< "keyToValue:"<< m.keyToValue("VeryHigh");
qDebug()<< "valueToKey:"<< m.valueToKey(MyEnum::VeryHigh);
qDebug()<< "keyCount:"<< m.keyCount();

输出是

MyEnum::High MyEnum::Low
keyToValue: 4
valueToKey: VeryHigh
keyCount: 4 

可见,使用Q_ENUM注册过的枚举类型,可以不加修饰直接被qDebug()打印出来,另外通过静态函数QMetaEnum::fromType()可以获得一个QMetaEnum 对象,以此作为中介,能够轻松完成枚举量和字符串之间的相互转化。 这一点恐怕是引入Q_ENUM机制最直接的好处。
除此以外,QMetaEnum还提供了一个内部的索引,从1开始给每个枚举量按自然数顺序编号(注意和枚举量本身的数值是两回事),提供了int value(int index) 和const char *key(int index)
两个便捷函数分别返回枚举量对应的数值和枚举量对应的字符串,配合keyCount() 函数可以实现枚举量的遍历:

qDebug()<<m.name()<<":";
for (int i = 0; i < m.keyCount(); ++i) {
    qDebug()<<m.key(i)<<m.value(i);
}

其中name()函数返回枚举类型名字。
Q_ENUM使用起来很很简单,对不对?但是还是有几个注意事项需要说明:

  1. Q_ENUM只能在使用了Q_OBJECT或者Q_GADGET的类中,类可以不继承自QObject,但一定要有上面两个宏之一(Q_GADGET是Q_OBJECT的简化版,提供元对象的一部分功能,但不支持信号槽);
  2. Q_ENUM宏只能放置于所包含的结构体定义之后,放在前面编译器会报错,结构体定义和Q_ENUM宏之间可以插入其他语句,但不建议这样做;
  3. 一个类头文件中可以定义多个Q_ENUM加持的结构体,结构体和Q_ENUM是一一对应的关系;
  4. Q_ENUM加持的结构体必须是公有的;
  5. Q_ENUM宏引入自Qt5.5版本,之前版本的Qt请使用Q_ENUMS宏,但Q_ENUMS宏不支持QMetaEnum::fromType()函数(这也是Q_ENUMS被弃用的原因)。

Q_FLAG的引入解决什么问题?

除了Q_ENUM,Qt中还有另一个类似的宏——Q_FLAG,着力弥补C++中结构体无法组合使用,和缺乏类型检查的缺点,怎么理解呢?我们看一个例子:
在经典C++中,如果我们要定义一个表示方位的结构体:

enum Orientation
{ 
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()<<(MyEnum::Up|MyEnum::Down);
QMetaEnum m = QMetaEnum::fromType<MyEnum::OrientationFlags>();  //since Qt5.5
qDebug()<< "keyToValue:"<<m.keyToValue("Up|Down");
qDebug()<< "valueToKey:"<<m.valueToKey(Up|Down);
qDebug()<< "keysToValue:"<<m.keysToValue("Up|Down");
qDebug()<< "valueToKeys:"<<m.valueToKeys(Up|Down)<<endl;

qDebug()<< "isFlag:"<<m.isFlag();
qDebug()<< "name:"<<m.name();
qDebug()<< "enumName:"<<m.enumName();               //since Qt5.12
qDebug()<< "scope:"<<m.scope()<<endl;

执行结果

QFlags<MyEnum::Orientation>(Up|Down)
keyToValue: -1
valueToKey: 
keysToValue: 3
valueToKeys: "Up|Down" 

isFlag: true
name: OrientationFlags
enumName: Orientation
scope: MyEnum 

可以看到,经过Q_FLAG包装之后,QMetaEnum具有了操作复合枚举量的能力,注意这时应当使用keysToValue()和valueToKeys()函数, 取代之前的keyToValue()和valueToKey()函数。另外,isFlag()函数返回值变成了true,name()和enumName()分别返回Q_FLAG包装后和包装前的结构名。
实际上此时类中是存在两个结构体的,如果在定义时加上了Q_ENUM(Orientation),则Orientation和OrientationFlags都能被QMetaEnum识别并使用,只不过通常我们只关注Q_FLAG包装后的结构体。
这样我们顺便明白了Qt官方定义的许多枚举结构都是成对出现的原因,比如

enum Qt::AlignmentFlag
flags Qt::Alignment
enum Qt::MatchFlag
flags Qt::MatchFlags
enum Qt::MouseButton
flags Qt::MouseButtons

再总结下Q_FLAG以及Q_DECLARE_FLAGS、Q_DECLARE_OPERATORS_FOR_FLAGS使用的要点吧:

  1. Q_DECLARE_FLAGS(Flags, Enum)宏将普通结构体Enum重新定义成了一个可以自由进行与或非操作的安全的结构体Flags。Q_DECLARE_FLAG出现在Enum定义之后,且定义之后Enum和Flags是同时存在的;
  2. Q_DECLARE_OPERATORS_FOR_FLAGS(Flags)赋予了Flags一个全局操作符“|”,没有这个宏语句,Flags量之间进行与操作后的结果将是一个int值,而不是Flags值。Q_DECLARE_OPERATORS_FOR_FLAGS应当定义在类外;
  3. Q_DECLARE_OPERATORS_FOR_FLAGS只提供了“或”操作,没有提供“与”“非”操作;
  4. Q_DECLARE_FLAGS和Q_DECLARE_OPERATORS_FOR_FLAGS都是和元对象系统无关的,可以脱离Q_FLAG单独使用,事实上这两个宏在Qt4就已经存在(不确定更早是否存在),而Q_FLAG是在Qt5.5版本才加入的;
  5. 如果在我们的程序中不需要枚举变量的组合扩展,那么只用简单的Q_ENUM就好了。

Q_NAMESPACE,Q_ENUM_NS和Q_FLAG_NS

在Qt5.8之后,Qt引入了Q_NAMESPACE宏,这个宏能够让命名空间具备简化的元对象能力,但不支持信号槽(类似类里的Q_GADGET)。
在使用了Q_NAMESPACE的命名空间中,可以使用Q_ENUM_NS和Q_FLAG_NS,实现类中Q_ENUM和Q_FLAG的功能。
看一个例子:

namespace MyNamespace
{
Q_NAMESPACE
enum Priority
{
    High = 1,
    Low = 2,
    VeryHigh = 4,
    VeryLow = 8,
};
Q_ENUM_NS(Priority)			//如不使用Priority,可省略
Q_DECLARE_FLAGS(Prioritys, Priority)
Q_FLAG_NS(Prioritys)
Q_DECLARE_OPERATORS_FOR_FLAGS(Prioritys)
}

命名空间中Q_ENUM_NS和Q_FLAG_NS的使用和之前相类似,不再赘述。Q_DECLARE_OPERATORS_FOR_FLAGS则需要定义在命名空间之内。
使用代码:

using namespace MyNamespace;
qDebug()<<(High|Low);
QMetaEnum m = QMetaEnum::fromType<MyNamespace::Prioritys>();  //since Qt5.5
qDebug()<< "keyToValue:"<<m.keyToValue("High|Low");
qDebug()<< "valueToKey:"<<m.valueToKey(High|Low);

qDebug()<< "keysToValue:"<<m.keysToValue("High|Low");
qDebug()<< "valueToKeys:"<<m.valueToKeys(High|Low)<<endl;

qDebug()<< "isFlag:"<<m.isFlag();
qDebug()<< "name:"<<m.name();
qDebug()<< "enumName:"<<m.enumName();               //since Qt5.12
qDebug()<< "scope:"<<m.scope()<<endl;

运行结果

QFlags<MyNamespace::Priority>(High|Low)
keyToValue: -1
valueToKey: 
keysToValue: 3
valueToKeys: "High|Low" 

isFlag: true
name: Prioritys
enumName: Priority
scope: MyNamespace 

可以看到,从定义到使用,和之前Q_FLAG几乎一模一样。
在命名空间中使用Q_ENUM_NS或者Q_FLAG_NS,能让枚举结构体定义不再局限在类里,使我们有更多的选择。另外,在今后Qt的发展中,相信Q_NAMESPACE能带来更多的功能,我们拭目以待。

新旧对比

Qt一直是一个发展的框架,不断有新特性加入,使得Qt变得更易用。
本文介绍的内容都是在Qt5.5版本以后才引入的,Q_NAMESPACE的内容甚至要到Qt5.8版本才引入,在之前Qt中也存在着枚举量的扩展封装——主要是Q_ENUMS和Q_FLAGS,这套系统虽然已经不建议使用,但是为了兼容性,还是予以保留。我们看看之前的系统如何使用的:
枚举量定义基本一致,就是Q_ENUMS(Enum)宏放到定义之前,代码从略。
使用上:

QMetaObject object = MyEnum::staticMetaObject;				//before Qt5.5
int index = object.indexOfEnumerator("Orientation");
QMetaEnum m = object.enumerator(index);

对比改进后的

QMetaEnum m = QMetaEnum::fromType<MyEnum::Orientation>();  //since Qt5.5

不仅仅是3行代码简化成1行,更重要的是Qt程序员终于不用再显式调用元对象QMetaObject了。改进的代码元对象机制同样在起着作用,但却变得更加隐蔽,更加沉默,使得程序员可以把精力更多放到功能的实现上,这大概就是Qt发展的方向。

Chapter2 【QT】枚举常用宏

原文链接:https://blog.csdn.net/WL0616/article/details/131376253

  1. Q_FLAG宏
    1.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
};

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

 typedef QFlags<Enum> 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 <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() {}

在这里插入图片描述

Chapter3 Qt中自定义结构体、枚举型做信号参数传递

原文链接:https://blog.csdn.net/ZefinNg/article/details/109171286

参考链接

如何在Qt信号和槽中使用枚举
https://cloud.tencent.com/developer/ask/sof/120786

问题

当自定义结构体、枚举通过信号的参数进行传递的时候,运行以后可能会报错“QObject::connect: Cannot queue arguments of type ‘XXX’”,此时信号与槽不一定会生效,但是编译过程是没问题的。因此,这样的问题比较麻烦,应该尽力规避。

解决

出现这样的问题是由于自定义的结构体、枚举,没有注册进qt的元对象系统,因此无法识别。
在声明结构体和枚举后应使用Q_ENUM()宏进行注册,再用qRegisterMetaType(“XXX”);进行注册。

示例

Communication.h如下:

class Communication : public QObject
{
    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::CONNECT_TYPE>("Communication::CONNECTTYPE");
    qRegisterMetaType<QAbstractSocket::SocketState>("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 <QtCore>

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 <QtCore>

#include "statemachine.h"

void StateMachine::setState(state newState)
{
    emit stateChanged(newState);
    emit testSignal();
}

该线程被定义为

// main.h
#ifndef _MAIN_H
#define _MAIN_H

#include <QtCore>

#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 <QtCore>
#include <QApplication>

#include "statemachine.h"
#include "main.h"

void MyThread::run()
{
    myStateMachine = new StateMachine();

    qRegisterMetaType<StateMachine::state>("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() << "Test signal";
}

void MyThread::onNewState(StateMachine::state newState)
{
    qDebug() << "New state is:" << newState;
}

Chapter5 个人总结(2023-10)

user.h

#ifndef USER_H
#define USER_H

#include <QObject>

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();

    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>("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();
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1135508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎么在Python爬虫中使用IP代理以避免反爬虫机制?

在进行网络爬虫的过程中&#xff0c;尤其是在大规模批量抓取数据时&#xff0c;需要应对各种反爬虫技术&#xff0c;其中最常用的就是IP封锁。为了避免IP被封锁&#xff0c;我们可以使用IP代理来隐藏自己的真实IP地址&#xff0c;从而让爬虫活动看起来更像正常的浏览器行为。 I…

Table-GPT:让大语言模型理解表格数据

llm对文本指令非常有用&#xff0c;但是如果我们尝试向模型提供某种文本格式的表格数据和该表格上的问题&#xff0c;LLM更有可能产生不准确的响应。 在这篇文章中&#xff0c;我们将介绍微软发表的一篇研究论文&#xff0c;“Table-GPT: Table- tuning GPT for Diverse Table…

虹科 | 解决方案 | 汽车示波器 远程诊断方案

车厂总部专家实时指导你修车 当一线汽修技师遇到疑难问题无从下手时&#xff0c;可以准备好pico汽车示波器套装&#xff0c;并戴上我们的M400智能AR眼镜&#xff0c;通过语音操作&#xff0c;呼叫主机厂的技术支持老师&#xff1b;老师通过AR眼镜上的摄像头老师可以实时看到现…

Python数据挖掘:入门、进阶与实用案例分析——基于非侵入式负荷检测与分解的电力数据挖掘

文章目录 摘要01 案例背景02 分析目标03 分析过程04 数据准备05 属性构造06 模型训练07 性能度量08 推荐阅读赠书活动 摘要 本案例将根据已收集到的电力数据&#xff0c;深度挖掘各电力设备的电流、电压和功率等情况&#xff0c;分析各电力设备的实际用电量&#xff0c;进而为电…

财务RPA机器人真的能提高效率吗?

财务部门作为一个公司的管理职能部门承担着一个公司在商业活动中各个方面的重要职责。理论上来说&#xff0c;一个公司的财务部门的实际工作包含但不限于对企业的盈亏情况进行评估、对风险进行预测、通过数据分析把握好公司的财务状况、税务管理等。 然而&#xff0c;实际上在…

分析主题帆软决策报表控件实现点击查询按钮后才能查询

点击「我的分析」即进入分析主题的管理界面&#xff0c;如下图所示&#xff1a; 2.1.1 分析列表区域 「分析列表」存放着当前用户创建的和其他用户协作给该用户的所有分析主题。 可以对分析列表和分析主题进行管理&#xff0c;详情参见&#xff1a;管理我的分析、管理分析主…

oa系统是什么?有哪些功能和作用?

本文将为大家讲解&#xff1a;1、oa系统是什么&#xff1f;有哪些功能和作用&#xff1f; 一、什么是OA系统 OA系统全称为Office Automation&#xff0c;即办公自动化系统。它是一种专门为企业和机构的日常办公工作提供服务的综合性软件平台&#xff0c;具有信息管理、流程管…

微信小程序实现文章内容详情

方案一、使用微信小程序官方提供的webview 前提已经在微信公众平台开发管理配置好了安全域名即&#xff1a; 方案二、把网页转成pdf直接展示 前提已经在微信公众平台开发管理配置好了安全域名即&#xff1a; 实现思路是发起网络请求拿到pdf下载地址&#xff0c;然后wx.download…

Linux (KDE) 中使用Network Settings设置静态ip

在 Linux (KDE) 中使用 Network Settings 设置s5静态IP详细教程 。 首先&#xff0c;打开 KDE 的设置面板。可以通过点击桌面上的设置图标&#xff0c;或者在开始菜单中搜索 “Settings” 并打开。 在设置面板中&#xff0c;点击 “Network” 选项。 接下来&#xff0c;你会看…

AD20生产Gerber文件

1、打开所要导出文件的PCB文件。并选择制造输出Gerber文件。 2、选择单位和格式 3、设置需要输出的图层 点击确定后&#xff0c;就可以生成Gerber文件。如下图所示。 4、生成钻孔文件 5&#xff0c;选择PCB窗口&#xff0c;跟第一步一样&#xff1b; 6&#xff0c;点击菜单 文…

讲座记录|1024学科周讲座分享

前言&#xff1a;2023年10月24日&#xff0c;我参加了学院举办的学科周活动&#xff0c;本篇文章记录一下参加本次活动的收获。 本次活动中&#xff0c;我主要参加了黄萱菁教授和高跃教授的讲座以及几位老师的Panel环节&#xff0c;然后聆听了网络空间安全分论坛的报告。在本次…

MySQL进阶(再论事务)——什么是事务 事务的隔离级别 结合MySQL案例详细分析

前言 MySQL最为最流行的开源数据库&#xff0c;其重要性不言而喻&#xff0c;也是大多数程序员接触的第一款数据库&#xff0c;深入认识和理解MySQL也比较重要。 本篇博客阐述MySQL的事务的定义和特性&#xff0c;原子性&#xff0c;一致性&#xff0c;隔离性&#xff0c;持久…

在 Windows 用 Chrome System Settings 设置代理

在 Windows 用 Chrome System Settings 设置代理 贴心提示&#xff1a;在设置代理之前&#xff0c;请确保您已经安装了 浏览器。 &#x1f527; 设置代理的详细步骤如下&#xff1a; 打开 浏览器&#xff0c;输入 //settings/system 并回车。 在「系统和网络设置」页面中&am…

突然发现柚子租车v1.42的小程序后端代码竟然内核文件全加密了!记录我的解密过程

因客户需求&#xff0c;需要定制租车小程序源码&#xff0c;找了很多要么很贵要么功能不满足需求&#xff0c;为啥这么折腾&#xff1f;还不是因为客户给的资金有限&#xff01; 翻来覆去找到了柚子租车这个小程序功能各方面都能满足需求&#xff0c;然后我就尝试找最新版&…

Find My眼镜|苹果Find My技术与眼镜结合,智能防丢,全球定位

近年来&#xff0c;得益于国家政策的大力支持和技术的不断进步&#xff0c;眼镜产品的发展取得了长足的进步&#xff0c;正朝着高质量、多元化方向迈进。在这个趋势下&#xff0c;眼镜的功能性、时尚性、舒适性等方面都有了明显的提升&#xff0c;同时为了满足不同年龄段消费者…

[黑马程序员SpringBoot2]——基础篇2

目录&#xff1a; 模块创建实体类快速开发(lombok)数据层标准开发&#xff08;基础CRUD)开启MP运行日志分页数据层标准开发(条件查询)业务层标准开发&#xff08;基础CRUD)业务层标准开发&#xff08;基于MyBatisPlus构建)表现层标准开发表现层数据一致性处理&#xff08;R对象…

阿里云企业邮箱基于Spring Boot快速实现发送邮件功能

邮件在项目中经常会被用到&#xff0c;比如用邮件发送通知。比如&#xff0c;通过邮件注册、认证、找回密码、系统报警通知、报表信息等。本篇文章带大家通过SpringBoot快速实现一个发送邮件的功能。 邮件协议 下面先简单了解一下常见的邮件协议。常用的电子邮件协议有SMTP、…

xxl-job项目集成实战,全自动项目集成,可以直接使用到项目中

如果你看官方文档&#xff0c;在研究透&#xff0c;至少也得几天时间&#xff0c;如果你直接看我的文档&#xff0c;快速用到项目中&#xff0c;也就10分钟就搞好了。 xxl-job功能确实很强大&#xff0c;而且使用的人比较多&#xff0c;既然在使用xxl-job&#xff0c;那肯定是…

成为CSS选择器大师,让你的网页瞬间提升品味!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、选…

【SpringCoud】

1,启动各个服务: 2,访问以下地址: http://localhost:5001/product/purchase/1/1/1000 浏览器输出: 控制台输出: 这里随机调用的端口号使用了轮询的方式 从服务治理中心拉去一份服务实例清单,然后通过某种负载均衡的算法选择具体的实例 Product核心逻辑代码: @RestController…