QT 信号槽connect中解决自定义数据类型或数组作为函数参数的问题——QT qRegisterMetaType 注册MetaType——关键:注册自定义数据类型或QMap等容器类
2017-02-21 09:53
876 查看
一般情况下信号槽直接连接方式不会出现问题,但是如果信号与槽在不同线程或Qt::QueuedConnection方式连接,可能会在连接期间报以下类似问题,如:
QObject::connect: Cannot queue arguments of type 'ThreadSignal'
(Make sure 'ThreadSignal' is registered using qRegisterMetaType().)
或者
QObject::connect: Cannot queue arguments of type 'BYTE[5]'
(Make sure 'BYTE[5]' is registered using qRegisterMetaType().)
或者
QObject::connect: Cannot queue arguments of type 'QMap<QString,CommDevice*>'
(Make sure 'QMap<QString,CommDevice*>' is registered using qRegisterMetaType().)
出现如此问题,在于QT对数据类型未知,按照此提示在连接信号与槽之前,调用 qRegisterMetaType()解决。直接上代码,如下:
qRegisterMetaType<ThreadSignal>("ThreadSignal");
或者
qRegisterMetaType<BYTE * >("BYTE[5]"); //请注意这一行,关于如何注册数组类型
或者
这种情况有点复杂,由于QMap<QString,CommDevice*> 在qRegisterMetaType<QMap<QString,CommDevice*>>("QMap<QString,CommDevice*>")会报错,无法识别,故可才采用别名的方式:
typedef QMap<QString,CommDevice*> MP_COMMDEVICES;
然后注册采用
qRegisterMetaType<MP_COMMDEVICES>("MP_COMMDEVICES");即可注册成功。
关键点:此处的注册语句qRegisterMetaType()一定要在connect之前执行。如果是信号槽在不同线程的情况下,则需要采用以下方式先利用信号槽调用qRegisterMetaType()注册,然后再利用信号槽建立connect,而不是在构造函数中简单的将qRegisterMetaType语句放在connect语句之前执行这么简单,因为构造函数是在生成该对象的线程中执行的。具体参考以下代码:
注意:此处
原因:template argument deduction/substitution failed 模板函数的参数类型不能通过表达式推导
关于模板参数的推导,参考:【C++】模板参数推导(template argument deduction) http://www.cnblogs.com/visayafan/archive/2011/11/27/2265400.html
其实在信号槽连接方式使用Qt:QueuedConnection时,其中的参数完全没有必要使用引用类型,因为此种方式下,信号参数为引用类型,则还是会另外复制一份的。
其实不止是自定义类型,包括QList、QMap这种QT的容器类也是一样需要注册。估计QT只是给少数几个类如QString注册了。还有少数原生类型,比如发现__int64也是需要注册的,qRegisterMetaType<__int64>("__int64");。
另外有个建议:就是使用信号和槽的时候,尽量使用QT而不是标准库的容器类,比如QString、QList等等。这主要是出于性能上的考虑。QT的容器包括QString都使用了implicitly shared技术,所以拷贝构造函数运行速度是很快的。很适用于信号槽这种封包机制。因为封包本质上就是把函数的地址和函数的所有入参都保存起来,所以免不了调用函数入参的拷贝构造函数。
注意:
关于网上搜说的另一种解决办法:connect时添加参数Qt::DirectConnection,以保证其不被放入信号队列,不告警从而达到想要的效果,但这种办法不一定能解决该问题,因为如果是信号槽在不同线程执行,目的就是要让槽在另一个线程队列执行,如果采用Qt::DirectConnection,则槽函数将在发出信号的线程中执行,这与采用多线程的目的不符,故不建议这样使用。具体学习“Qt信号槽的几种连接方式和执行方式” http://www.dushibaiyu.com/2015/07/qt-signals-slots-connect.html
参考资料:
1、http://blog.csdn.net/runyon1982/article/details/49018855
2、http://www.tuicool.com/articles/yEZBv2
3、http://www.dushibaiyu.com/2015/07/qt-signals-slots-connect.html
4、http://www.cnblogs.com/visayafan/archive/2011/11/27/2265400.html
QObject::connect: Cannot queue arguments of type 'ThreadSignal'
(Make sure 'ThreadSignal' is registered using qRegisterMetaType().)
或者
QObject::connect: Cannot queue arguments of type 'BYTE[5]'
(Make sure 'BYTE[5]' is registered using qRegisterMetaType().)
或者
QObject::connect: Cannot queue arguments of type 'QMap<QString,CommDevice*>'
(Make sure 'QMap<QString,CommDevice*>' is registered using qRegisterMetaType().)
出现如此问题,在于QT对数据类型未知,按照此提示在连接信号与槽之前,调用 qRegisterMetaType()解决。直接上代码,如下:
qRegisterMetaType<ThreadSignal>("ThreadSignal");
或者
qRegisterMetaType<BYTE * >("BYTE[5]"); //请注意这一行,关于如何注册数组类型
或者
这种情况有点复杂,由于QMap<QString,CommDevice*> 在qRegisterMetaType<QMap<QString,CommDevice*>>("QMap<QString,CommDevice*>")会报错,无法识别,故可才采用别名的方式:
typedef QMap<QString,CommDevice*> MP_COMMDEVICES;
然后注册采用
qRegisterMetaType<MP_COMMDEVICES>("MP_COMMDEVICES");即可注册成功。
关键点:此处的注册语句qRegisterMetaType()一定要在connect之前执行。如果是信号槽在不同线程的情况下,则需要采用以下方式先利用信号槽调用qRegisterMetaType()注册,然后再利用信号槽建立connect,而不是在构造函数中简单的将qRegisterMetaType语句放在connect语句之前执行这么简单,因为构造函数是在生成该对象的线程中执行的。具体参考以下代码:
TcpConnectManage.h #ifndef TCPCONNECTMANAGE_H #define TCPCONNECTMANAGE_H #include <QObject> #include <QThread> #include "Protocol.h" typedef QMap<QString,CommDevice*> MP_COMMDEVICES;//QMap<QString,CommDevice*>无法在qRegisterMetaType中识别,故采用别名方式 class TcpConnectManage : public QObject { Q_OBJECT public: explicit TcpConnectManage(QObject *parent = 0); ...... void connectKeep(QMap<QString, Protocol *> *protocols, MP_COMMDEVICES comm_devices); ...... /// /// \brief registerReflex 注册反射类 /// void registerReflex(); /// /// \brief createConnect 本函数配合对应的信号槽的目的主要是,为了注册 /// void createConnect(); signals: void sigConnectKeep(QMap<QString, Protocol *> *protocols, MP_COMMDEVICES comm_devices); void sigRegisterReflex(); void sigCreateConnect(); public slots: void sltConnectKeep(QMap<QString, Protocol *> *protocols, MP_COMMDEVICES comm_devices); void sltRegisterReflex(); void sltCreateConnect(); }; #endif // TCPCONNECTMANAGE_H
#include "TcpConnectManage.h" TcpConnectManage::TcpConnectManage(QObject *parent) : QObject(parent) { connect(this,SIGNAL(sigRegisterReflex()),this,SLOT(sltRegisterReflex())); connect(this,SIGNAL(sigCreateConnect()),this,SLOT(sltCreateConnect())); } ...... void TcpConnectManage::registerReflex() { emit sigRegisterReflex(); } void TcpConnectManage::createConnect() { emit sigCreateConnect(); } ...... void TcpConnectManage::sltRegisterReflex() { //信号槽中使用的自定义类型注册 qRegisterMetaType<MP_COMMDEVICES>("MP_COMMDEVICES"); //类注册 ...... } void TcpConnectManage::sltCreateConnect() { connect(this,SIGNAL(sigConnectKeep(QMap<QString,Protocol*>*,MP_COMMDEVICES)), this,SLOT(sltConnectKeep(QMap<QString,Protocol*>*,MP_COMMDEVICES))); }
注意:此处
typedef QMap<QString,CommDevice*> MP_COMMDEVICES;//QMap<QString,CommDevice*>无法在qRegisterMetaType中识别,故采用别名方式 不能为
typedef QMap<QString,CommDevice*>& MP_COMMDEVICES;//QMap<QString,CommDevice*>无法在qRegisterMetaType中识别,故采用别名方式 即参数为引用类型,会报以下错误
原因:template argument deduction/substitution failed 模板函数的参数类型不能通过表达式推导
关于模板参数的推导,参考:【C++】模板参数推导(template argument deduction) http://www.cnblogs.com/visayafan/archive/2011/11/27/2265400.html
其实在信号槽连接方式使用Qt:QueuedConnection时,其中的参数完全没有必要使用引用类型,因为此种方式下,信号参数为引用类型,则还是会另外复制一份的。
其实不止是自定义类型,包括QList、QMap这种QT的容器类也是一样需要注册。估计QT只是给少数几个类如QString注册了。还有少数原生类型,比如发现__int64也是需要注册的,qRegisterMetaType<__int64>("__int64");。
另外有个建议:就是使用信号和槽的时候,尽量使用QT而不是标准库的容器类,比如QString、QList等等。这主要是出于性能上的考虑。QT的容器包括QString都使用了implicitly shared技术,所以拷贝构造函数运行速度是很快的。很适用于信号槽这种封包机制。因为封包本质上就是把函数的地址和函数的所有入参都保存起来,所以免不了调用函数入参的拷贝构造函数。
注意:
关于网上搜说的另一种解决办法:connect时添加参数Qt::DirectConnection,以保证其不被放入信号队列,不告警从而达到想要的效果,但这种办法不一定能解决该问题,因为如果是信号槽在不同线程执行,目的就是要让槽在另一个线程队列执行,如果采用Qt::DirectConnection,则槽函数将在发出信号的线程中执行,这与采用多线程的目的不符,故不建议这样使用。具体学习“Qt信号槽的几种连接方式和执行方式” http://www.dushibaiyu.com/2015/07/qt-signals-slots-connect.html
参考资料:
1、http://blog.csdn.net/runyon1982/article/details/49018855
2、http://www.tuicool.com/articles/yEZBv2
3、http://www.dushibaiyu.com/2015/07/qt-signals-slots-connect.html
4、http://www.cnblogs.com/visayafan/archive/2011/11/27/2265400.html
相关文章推荐
- QT 自定义信号与信号槽方法及解决自定义数据类型或数组作为函数参数的问题
- QT 自定义信号与信号槽方法及解决自定义数据类型或数组作为函数参数的问题
- Qt学习:线程间共享数据(使用信号槽传递数据,必须提前使用qRegisterMetaType来注册参数的类型)
- 信号槽所用的参数类型,必须是Qt能认识的元类型,否则就要调用Q_DECLARE_METATYPE和qRegisterMetaType进行注册
- Qt 线程间信号槽传递自定义数据类型(qRegisterMetaType的使用)
- Qt使用自定义类型信号槽注册问题
- Oracle自定义函数解决数据类型转换出错的问题
- 信号槽传递非Qt库类型参数时,出现QObject::connect: Cannot queue arguments of type 'QUuid'(Make sure 'string' is regi
- Q_DECLARE_METATYPE(继承QObject的类都已经自动注册),注册后的类型可以作为QVariant的自定义类型
- 关于在用curl函数post网页数据时,遇上表单提交 type为submit 类型而且没有name和id时可能遇到的问题及其解决方法
- 2个自定义的PHP in_array 函数,解决大量数据判断in_array的效率问题
- access中,有无将文本类型的数据格式转化成数值型的函数?(解决 order by 的非数字排序问题)
- mina自定义编解码器接收处理byte数组(同时解决数据传输中的粘包、缺包问题)
- 解决Zabbix自定义用户参数无法获取到数据的问题
- C/C++面试题--数组作为函数参数的几个问题
- C语言中数组作为函数参数的问题
- 使用mssql2008新特性(存储过程参数类型使用"用户自定义表"来实现批量DML更新多表)解决项目里遇到的性能问题
- 关于 Numpy 数组作为函数参数的一个小问题
- Qt注册元类型(MetaType)
- 关于结构指针类型数组做函数参数的一个问题#include<stdio.h>typede