QT 信号槽connect中解决自定义数据类型或数组作为函数参数的问题——QT qRegisterMetaType 注册MetaType——关键:注册自定义数据类型或QMap等容器类
一般情况下信号槽直接连接方式不会出现问题,但是如果信号与槽在不同线程或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语句之前执行这么简单,因为构造函数是在生成该对象的线程中执行的。具体参考以下代码:
1 TcpConnectManage.h 2 3 #ifndef TCPCONNECTMANAGE_H 4 #define TCPCONNECTMANAGE_H 5 6 #include <QObject> 7 #include <QThread> 8 #include "Protocol.h" 9 10 11 typedef QMap<QString,CommDevice*> MP_COMMDEVICES;//QMap<QString,CommDevice*>无法在qRegisterMetaType中识别,故采用别名方式 12 13 class TcpConnectManage : public QObject 14 { 15 Q_OBJECT 16 public: 17 explicit TcpConnectManage(QObject *parent = 0); 18 ...... 19 void connectKeep(QMap<QString, Protocol *> *protocols, MP_COMMDEVICES comm_devices); 20 ...... 21 /// 22 /// \brief registerReflex 注册反射类 23 /// 24 void registerReflex(); 25 /// 26 /// \brief createConnect 本函数配合对应的信号槽的目的主要是,为了注册 27 /// 28 void createConnect(); 29 30 signals: 31 void sigConnectKeep(QMap<QString, Protocol *> *protocols, MP_COMMDEVICES comm_devices); 32 void sigRegisterReflex(); 33 void sigCreateConnect(); 34 public slots: 35 void sltConnectKeep(QMap<QString, Protocol *> *protocols, MP_COMMDEVICES comm_devices); 36 void sltRegisterReflex(); 37 void sltCreateConnect(); 38 }; 39 40 #endif // TCPCONNECTMANAGE_H
1 #include "TcpConnectManage.h" 2 7 8 TcpConnectManage::TcpConnectManage(QObject *parent) : 9 QObject(parent) 10 { 11 connect(this,SIGNAL(sigRegisterReflex()),this,SLOT(sltRegisterReflex())); 12 connect(this,SIGNAL(sigCreateConnect()),this,SLOT(sltCreateConnect())); 13 } 14 15 ...... 16 17 void TcpConnectManage::registerReflex() 18 { 19 emit sigRegisterReflex(); 20 } 21 22 void TcpConnectManage::createConnect() 23 { 24 emit sigCreateConnect(); 25 } 26 27 ...... 28 29 void TcpConnectManage::sltRegisterReflex() 30 { 31 //信号槽中使用的自定义类型注册 32 qRegisterMetaType<MP_COMMDEVICES>("MP_COMMDEVICES"); 33 34 //类注册 35 ...... 36 } 37 38 void TcpConnectManage::sltCreateConnect() 39 { 40 connect(this,SIGNAL(sigConnectKeep(QMap<QString,Protocol*>*,MP_COMMDEVICES)), 41 this,SLOT(sltConnectKeep(QMap<QString,Protocol*>*,MP_COMMDEVICES))); 42 }
注意:此处
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时,其中的参数完全没有必要使用引用类型,因为此种方式下,信号参数为引用类型,则还是会另外复制一份的。
http://www.dushibaiyu.com/2015/07/qt-signals-slots-connect.html