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時,其中的參數完全沒有必要使用引用類型,因為此種方式下,信號參數為引用類型,則還是會另外復制一份的。

 

       其實不止是自定義類型,包括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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM