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信号和槽中使用枚举


理论基础:

一、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()<< 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 
#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<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 

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<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;
}

你可能感兴趣的:(Qt经验总结,工控软件,qt,开发语言)