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

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...0001Down = 0x02, //即0000...0010Left = 0x04, //即0000...0100Right = 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...0001Down = 2, //即0000...0010Left = 4, //即0000...0100Right = 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); // OKlabel->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_OBJECTpublic:enum Orientation {Up = 0x01,    //即0000...0001Down = 0x02,  //即0000...0010Left = 0x04,  //即0000...0100Right = 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的keyint valueIndex = metaEnum.value(0);                                //下标为0的valueQString Key = metaEnum.valueToKeys(MyEnum::Left | MyEnum::Right);  //通过value得到keyint value = metaEnum.keysToValue("Up | Down");                     //通过key得到valueqDebug() << "枚举的名称:" << 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_OBJECTQ_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_OBJECTprivate: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 workconnect(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.mzph.cn/news/118813.shtml

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

相关文章

Pytorch指定数据加载器使用子进程

torch.utils.data.DataLoader(train_dataset, batch_sizebatch_size, shuffleTrue,num_workers4, pin_memoryTrue) num_workers 参数是 DataLoader 类的一个参数&#xff0c;它指定了数据加载器使用的子进程数量。通过增加 num_workers 的数量&#xff0c;可以并行地读取和预处…

如何将音频与视频分离

您一定经历过这样的情况&#xff1a;当你非常喜欢视频中的背景音乐时&#xff0c;希望将音频从视频中分离出来&#xff0c;以便你可以在音乐播放器中收听音乐。有没有一种有效的方法可以帮助您快速从视频中提取音频呢&#xff1f;当然是有的啦&#xff0c;在下面的文章中&#…

根据输入类型来选择函数不同的实现方法functools.singledispatch

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 根据输入类型来选择函数不同的实现方法 functools.singledispatch 输入6后&#xff0c;下列输出正确的是&#xff1f; from functools import singledispatch singledispatch def calcu…

树莓派系统文件解析

title: “树莓派系统文件分析” date: 2023-10-25 permalink: /posts/2023/10/blog-post-5/ tags: 树莓派 本篇blog来分析和总结下树莓派系统文件以及他们的作用。使用的系统是Raspberry Pi OS with desktop System: 64-bitKernel version: 6.1Debian version: 12 (bookworm)…

经典链表试题(二)

文章目录 一、移除链表元素1、题目介绍2、思路讲解3、代码实现 二、反转链表1、题目介绍2、思路讲解3、代码实现 三、相交链表1、题目介绍2、思路讲解3、代码实现 四、链表的中间结点1、题目介绍2、思路讲解3、代码实现 五、设计循环队列1、题目介绍2、思路讲解3、代码实现 六、…

2023高频前端面试题-http

1. HTTP有哪些⽅法&#xff1f; HTTP 1.0 标准中&#xff0c;定义了3种请求⽅法&#xff1a;GET、POST、HEAD HTTP 1.1 标准中&#xff0c;新增了请求⽅法&#xff1a;PUT、PATCH、DELETE、OPTIONS、TRACE、CONNECT 2. 各个HTTP方法的具体作用是什么&#xff1f; 方法功能G…

『C语言进阶』动态内存管理

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f516;系列专栏&#xff1a; C语言、Linux、Cpolar ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 前言一、动态内存函数的介绍1.1 malloc和free函数1.2 calloc函数1.3 realloc函数 二、常见的动态内存错误2.1 …

行业模型应该如何去拆解?

行业模型应该如何去拆解&#xff1f; 拆解行业模型是一个复杂的过程&#xff0c;涉及对整个行业的深入分析和理解。下面是一些步骤和方法&#xff0c;可以帮助你系统地拆解行业模型&#xff1a; 1. 确定行业范围 定义行业&#xff1a;明确你要分析的行业是什么&#xff0c;包括…

React中的Virtual DOM(看这一篇就够了)

文章目录 前言了解Virtual DOMreact创建虚拟dom的方式React Element虚拟dom的流程虚拟dom和真实dom的对比后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;react合集 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌…

在pycharm中创建python模板文件

File——>Setting——>File and Code Templates——>Python Scripts 在文本框中输入模板内容

vue首页多模块布局(标题布局)

<template><div class"box"><div class"content"><div class"box1" style"background-color: rgb(245,23,156)">第一个</div><div class"box2" style"background-color: rgb(12,233,…

windows下使用FFmpeg开源库进行视频编解码完整步聚

最终解码效果: 1.UI设计 2.在控件属性窗口中输入默认值 3.复制已编译FFmpeg库到工程同级目录下 4.在工程引用FFmpeg库及头文件 5.链接指定FFmpeg库 6.使用FFmpeg库 引用头文件 extern "C" { #include "libswscale/swscale.h" #include "libavdevic…

composer安装thinkphp6报错

composer安装thinkphp6报错&#xff0c; 查看是否安装了对应的PHP扩展&#xff0c;我这边使用的是宝塔的环境&#xff0c;全程可以可视化操作 这样就可以安装完成了

【AIGC】百度文库文档助手之 - 一键生成PPT

百度文库文档助手之 - 一键生成PPT 引言一、文档助手&#xff1a;体验一键生成PPT二、文档助手&#xff1a;进阶用法三、其它生成PPT的方法3.1 ChatGPT3.2 文心一言 引言 就在上个月百度文库升级为一站式智能文档平台&#xff0c;开放四大AI能力&#xff1a;智能PPT、智能总结、…

【Ansible自动化运维工具 第一部分】Ansible常用模块详解(附各模块应用实例和Ansible环境安装部署)

Ansible常用模块 一、Ansible1.1 简介1.2 工作原理1.3 Ansible的特性1.3.1 特性一&#xff1a;Agentless&#xff0c;即无Agent的存在1.3.2 特性二&#xff1a;幂等性 1.4 Ansible的基本组件 二、Ansible环境安装部署2.1 安装ansible2.2 查看基本信息2.3 配置远程主机清单 三、…

计算机中了mallox勒索病毒怎么办,勒索病毒解密,数据恢复

最近一段时间&#xff0c;云天数据恢复中心陆续收到很多企业的求助&#xff0c;企业的计算机服务器遭到了mallox勒索病毒攻击&#xff0c;导致企业的数据库无法正常使用&#xff0c;严重影响了企业的正常生产生活&#xff0c;为此&#xff0c;云天数据恢复中心的工程师通过对此…

【Java笔记+踩坑】设计模式——原型模式

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客​ 目录 零、经典的克隆羊问题&#xff08;复制10只属性相同的羊&#xff09; 一、传统方案&#xff1…

酒类商城小程序怎么做

随着互联网的快速发展&#xff0c;线上购物越来越普及。酒类商品也慢慢转向线上销售&#xff0c;如何搭建一个属于自己的酒类小程序商城呢&#xff1f;下面就让我们一起来看看吧&#xff01; 一、登录乔拓云平台 首先&#xff0c;我们需要进入乔拓云平台的后台&#xff0c;点击…

使用boost.mysql来操作mysql 数据库

准备条件 1. visual studio 2019 2. boost库 3. 安装本地的mysql 服务器&#xff0c;boost.mysql对mysql有版本要求最好8.0&#xff0c;具体参考官方文档 安装 使用Nuget安装boost 要安装 openssl&#xff0c;否则的话编译其他项目会产生依赖ssl的错误 安装mysql 省略 …

Java操作Excel

一、Java操作Excel 二、Excel根据单元格状态自动变更行背景颜色 用excel记录和跟进工作的时候&#xff0c;设置背景颜色&#xff0c;以此让记录更加突出&#xff0c;方便查看&#xff0c;但是手动修改背景颜色一来麻烦容易漏&#xff0c;也可能颜色不统一&#xff0c;我们可以试…