zoukankan      html  css  js  c++  java
  • 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     /// rief registerReflex       注册反射类
    23     ///
    24     void registerReflex();
    25     ///
    26     /// rief 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时,其中的参数完全没有必要使用引用类型,因为此种方式下,信号参数为引用类型,则还是会另外复制一份的。

           其实不止是自定义类型,包括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

     

  • 相关阅读:
    java—连连看-实现封装
    java—连连看GUI
    连连看--产生随机数
    勇者斗恶龙
    在ctex环境下利用Metapost作图
    [leetcode] Binary Tree Postorder Traversal
    [leetcode] Binary Tree Maximum Path Sum
    [leetcode] Binary Tree Level Order Traversal II
    [leetcode] Binary Tree Level Order Traversal
    [leetcode] Binary Tree Inorder Traversal
  • 原文地址:https://www.cnblogs.com/liushui-sky/p/6422643.html
Copyright © 2011-2022 走看看