Qt信号槽使用结构体作为参数

问题由来

定义一个结构体

struct myStruct
{
  int a;
  float b;
};
通过信号槽传递该结构体

connect(this, SIGNAL(m_signal(myStruct)), this, SLOT(m_slot(myStruct)));
这样做是行不通的,正确的做法:
通过Q_DECLARE_METATYPE声明自定义的结构体

struct myStruct
{
  int a;
  float b;
};
Q_DECLARE_METATYPE(myStruct);

然后以QVariant代替自定义的结构体

connect(this, SIGNAL(m_signal(QVariant)), this, SLOT(m_slot(QVariant)));
在发射信号前,将自定义结构体打包为QVariant

myStruct mstruct;
QVariant data;
data.setValue(mstruct);
emit signal_child(data);

在槽函数中,解析QVariant

myStruct mstruct = data.value<myStruct>();
 

注册元类型

信号可以带参数,参数的类型,必须是元对象系统能够识别的类型, 即元类型。

下面这几个类型是自动注册的,不需要使用Q_DECLARE_METATYPE这个宏:
1.QObject继承下来的子类的指针;
2.QList<T>, QVector<T>, QQueue<T>, QStack<T>, QSet<T> or QLinkedList<T>这些T都是自动注册的;
3.QHash<T1, T2>, QMap<T1, T2> or QPair<T1, T2> T1,和T2都是自动注册的;
4.QPointer<T>, QSharedPointer<T>, QWeakPointer<T>这3个T必须是QObject的子类;
5.枚举类型要用Q_ENUM or Q_FLAG;
6.拥有Q_GADGET宏的类。

例如:自定义结构体,connect想通过结构体参数来传递

struct _ColorBalance
{
    bool preserve_luminosity;
    int cyan_red[3];
    int magenta_green[3];
    int yellow_blue[3];
};

signals:
    void state_changed(_ColorBalance *color_balance);

connect(this, &ColorBalance::state_changed, pic, &Picture::ColorBalance);

Qt已经将大部分常用的基础类型,都注册进了元对象系统,可以在QMetaType类中看到。

通常写的继承于QObject的子类,本身已经附带了元信息,可以直接在信号-槽中使用。

不是继承于QObject的结构体、类等自定义类型,可以通过Q_DECLARE_METATYPE宏 和 qRegisterMetaType函数进行注册,之后就可以在信号-槽中使用。

官方文档

https://wiki.qt.io/New_Signal_Slot_Syntax

https://woboq.com/blog/new-signals-slots-syntax-in-qt5.html

https://woboq.com/blog/how-qt-signals-slots-work.html

https://woboq.com/blog/how-qt-signals-slots-work-part2-qt5.html

https://doc.qt.io/qt-5/signalsandslots-syntaxes.html

https://doc.qt.io/qt-5/qmetatype.html

int qRegisterMetaType()

Call this function to register the type T. T must be declared with Q_DECLARE_METATYPE(). Returns the meta type Id.

Example:

int id = qRegisterMetaType<MyStruct>();
This function requires that T is a fully defined type at the point where the function is called. For pointer types, it also requires that the pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able to register pointers to forward declared types.

After a type has been registered, you can create and destroy objects of that type dynamically at run-time.

To use the type T in QVariant, using Q_DECLARE_METATYPE() is sufficient. To use the type T in queued signal and slot connections, qRegisterMetaType<T>() must be called before the first connection is established.

Also, to use type T with the QObject::property() API, qRegisterMetaType<T>() must be called before it is used, typically in the constructor of the class that uses T, or in the main() function.

Q_DECLARE_METATYPE(Type)

This macro makes the type Type known to QMetaType as long as it provides a public default constructor, a public copy constructor and a public destructor. It is needed to use the type Type as a custom type in QVariant.

This macro requires that Type is a fully defined type at the point where it is used. For pointer types, it also requires that the pointed to type is fully defined. Use in conjunction with Q_DECLARE_OPAQUE_POINTER() to register pointers to forward declared types.

Ideally, this macro should be placed below the declaration of the class or struct. If that is not possible, it can be put in a private header file which has to be included every time that type is used in a QVariant.

Adding a Q_DECLARE_METATYPE() makes the type known to all template based functions, including QVariant. Note that if you intend to use the type in queued signal and slot connections or in QObject's property system, you also have to call qRegisterMetaType() since the names are resolved at runtime.

This example shows a typical use case of Q_DECLARE_METATYPE():

struct MyStruct
{
    int i;
    ...
};

Q_DECLARE_METATYPE(MyStruct)
If MyStruct is in a namespace, the Q_DECLARE_METATYPE() macro has to be outside the namespace:

namespace MyNamespace
{
    ...
}

Q_DECLARE_METATYPE(MyNamespace::MyStruct)
Since MyStruct is now known to QMetaType, it can be used in QVariant:

MyStruct s;
QVariant var;
var.setValue(s); // copy s into the variant

...

// retrieve the value
MyStruct s2 = var.value<MyStruct>();

Some types are registered automatically and do not need this macro:

Pointers to classes derived from QObject
QList<T>, QVector<T>, QQueue<T>, QStack<T>, QSet<T> or QLinkedList<T> where T is a registered meta type
QHash<T1, T2>, QMap<T1, T2> or QPair<T1, T2> where T1 and T2 are registered meta types
QPointer<T>, QSharedPointer<T>, QWeakPointer<T>, where T is a class that derives from QObject
Enumerations registered with Q_ENUM or Q_FLAG
Classes that have a Q_GADGET macro

 

举例说明

QT信号槽传递复杂参数

QT信号和槽以结构体为参数传递复杂数据

参考文献

Qt文档阅读笔记-关于Q_DECLARE_METATYPE原理以及使用

Qt 自定义数据类型qRegisterMetaType Q_DECLARE_METATYPE 的使用 (附demo)

Qt工作笔记-对connect的第五个参数的研究

Qt原理-窥探信号槽的实现细节

Qt实用技能5-掌握信号槽使用细节

本人的工程源码实践

https://download.csdn.net/download/libaineu2004/12307461

我发现Qt5怎么不需要通过Q_DECLARE_METATYPE声明自定义的结构体,也能编译通过,使用正常啊?!!

估计是多线程传递时,才需要使用Q_DECLARE_METATYPE。另外,看样子Q_DECLARE_METATYPE和QVariant要搭配使用。

发布了505 篇原创文章 · 获赞 610 · 访问量 344万+

猜你喜欢

转载自blog.csdn.net/libaineu2004/article/details/105332464