QMetaObject::connectSlotsByName: No matching signal for問題的解決方法

http://www.civilnet.cn/bbs/browse.php?topicno=4023

今天發現qt程序在運行時命令行窗口會彈出下面的警告:

QMetaObject::connectSlotsByName: No matching signal for ...

但實際功能並無受影響。網上google了一圈,終於找到緣由。

如下轉自:http://www.qtforum.org/article/20685/connectslotsbyname.html

After a bit of sleuthing to find the source of all the Qt warnings, 「QMetaObject::connectSlotsByName: No matching signal for …」, in our log window/file, here’s what I’ve found.

setupUi calls connectSlotsByName. connectSlotsByName parses the moc_ file in order to connect slots and signal s. The moc_ file contains a list of all the slots for the class. The parser iterates over the list of slot names looking for the following pattern: on_objectName_signal , where on_objectName_signal is the name of the slot, objectName is the object name and signal is the signal . For example, if you have a slot named, on_doneButton_clicked(), the parser looks for an object named doneButton, a signal named clicked and then connects the on_doneButton_clicked() slot to QButton’s signal clicked().

If you follow this naming convention, you do no t need to call the connect() method, no r do you need to connect the signal via the Qt UI editor in VisualStudio. connectSlotsByName will automatically make the connection for you.

So, in order to get rid of the 「No matching signal for …」 warnings, we need to either follow this naming convention, or make sure no ne of our slot names begin with 「on_」. If you have a slot, onDoneButton_clicked, for example, connectSlotsByName will no try to connect it with a signal , no r will emit an warning. 

上面大概就是 說:用VistalStudio裏的QtEditer能夠自動調用Qt中「connectSlotsByName」即「按空間名稱關聯槽」的方式進行關 聯,對應的函數必須寫成「on_控件名_信號名」的格式;或者也能夠經過connet函數人爲顯式地將信號和槽關聯起來。可是,若是採用顯式 connect的方法的同時,又將槽函數的名字起成了「on_控件名_信號名」的格式,那麼就會在運行時彈出 「QMetaObject::connectSlotsByName: No matching signal for」的警告了!



connectSlotsByName 是一個QMetaObject類裏的static函數,其定義以下: 

static void connectSlotsByName(QObject *o); 

其做用是如其名稱同樣,用來將QObject *o裏的子孫QObject的某些信號按照其objectName鏈接到o的槽上。


既然是根據objectName來鏈接信號和槽,那麼就有了幾個問題:

一、能不能對多個QObject設置一樣的objectName呢? 
二、若是能,那麼connectSlotsByName會鏈接多少個QObject的信號到指定的槽上呢?

有了疑問,第一個應該作的事情,固然是編寫代碼進行測試了。

在測試的主窗口類構造函數在「ui->setupUi(this); 」語句前編寫以下代碼:php



[cpp]  view plain  copy
  1. for(int i=0;i<9;++i)  
  2.     {  
  3.         QPushButton *btn=new QPushButton(this);  
  4.         btn->setObjectName(「TestButton」);  
  5.         qDebug(btn->objectName().toStdString().c_str());  
  6.     }  
  7.   
  8.     ui->setupUi(this);  


QMetaObject::connectSlotsByName()這個函數會在ui->setupUi(this);裏被調用執行。 

而後在主窗口裏增長下面的槽定義很代碼: 
html


[cpp]  view plain  copy
  1. void on_TestButton_clicked(bool bVal);  
  2.   
  3.     void MainWindow::on_TestButton_clicked(bool bVal)  
  4.     {  
  5.         QObject *obj=sender();  
  6.         qDebug("TestButton is clicked by %s!%d\n",obj->objectName().toStdString().c_str(),bVal);  
  7.     }  


而後編譯運行,結果出來了:

9個按鈕的objectName()都返回"TestButton" 
只有第一個按鈕的clicked信號被鏈接到了on_TestButton_clicked槽上 
第 一個結論與個人猜測相符(後來看了QObject的源碼,也是比較簡單的),第二個結論與個人猜測有點不一樣,我原本猜測,應該是9個按鈕的clicked 信號應該均可以鏈接到這個on_TestButton_clicked槽上的,可是卻只有第一個鏈接上了,這是爲何呢?

讓咱們看看connectSlotsByName都幹了些什麼吧。

connectSlotsByName的源碼解讀 
函數


1 void QMetaObject::connectSlotsByName(QObject *o)
2 {
3     if (!o)
4         return;
5     const QMetaObject *mo = o->metaObject();
6     Q_ASSERT(mo);
7     const QObjectList list = qFindChildren<QObject *>(o, QString());
8     for (int i = 0; i < mo->methodCount(); ++i) {
9         const char *slot = mo->method(i).signature();
10         Q_ASSERT(slot);
11         if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_')
12             continue;
13         bool foundIt = false;
14         for(int j = 0; j < list.count(); ++j) {
15             const QObject *co = list.at(j);
16             QByteArray objName = co->objectName().toAscii();
17             int len = objName.length();
18             if (!len || qstrncmp(slot + 3, objName.data(), len) || slot[len+3] != '_')
19                 continue;
20             const QMetaObject *smo = co->metaObject();
21             int sigIndex = smo->indexOfMethod(slot + len + 4);
22             if (sigIndex < 0) { // search for compatible signals
23                 int slotlen = qstrlen(slot + len + 4) - 1;
24                 for (int k = 0; k < co->metaObject()->methodCount(); ++k) {
25                     if (smo->method(k).methodType() != QMetaMethod::Signal)
26                         continue;
27 
28                     if (!qstrncmp(smo->method(k).signature(), slot + len + 4, slotlen)) {
29                         sigIndex = k;
30                         break;
31                     }
32                 }
33             }
34             if (sigIndex < 0)
35                 continue;
36             if (QMetaObject::connect(co, sigIndex, o, i)) {
37                 foundIt = true;
38                 break;
39             }
40         }
41         if (foundIt) {
42             // we found our slot, now skip all overloads
43             while (mo->method(i + 1).attributes() & QMetaMethod::Cloned)
44                   ++i;
45         } else if (!(mo->method(i).attributes() & QMetaMethod::Cloned)) {
46             qWarning("QMetaObject::connectSlotsByName: No matching signal for %s", slot);
47         }
48     }
49 } 
測試


看connectSlotsByName的實現,能夠注意到如下幾個地方:

第7行,取得o的全部子對象,在測試的代碼裏,QPushButton都設置了this爲父對象,因此它們顯然會在這個列表裏出現 
第8行,是一個遍歷o的方法的循環,o的信號和槽就在其中 
第11行,對於方法名稱不是"on_"開頭的方法跳過不處理,這也說明,若是你在一個QObject子類裏定義了"on_"開頭的槽的話,必定會被connectSlotsByName函數進行搜索匹配的操做的 
第14行開始到33行,開始遍歷o的全部的子對象,試圖匹配到與槽名稱以及信號名稱相應的子對象。首先取出其objectName()與槽名稱裏的第一個‘_’和第二個‘_’作名稱匹配。其次取出子對象的全部信號,與第二個‘_’以後部分作匹配。
若是匹配成功,則會執行36行的鏈接代碼。鏈接成功的話,就會在38行break中斷循環。 
看到第5點,已經很明瞭了,對於同名的控件,connectSlotsByName只會鏈接子對象鏈表裏的第一個對象的信號到槽上。

總結:

儘可能不要讓QObject出現相同objectName的狀況 
若是同名connectSlotsByName只能給其中一個創建缺省的信號和槽的鏈接 
若是出現大量編碼建立大量控件的狀況,最好是本身去創建信號和槽的鏈接,而不是依賴connectSlotsByName來作到這個工做。connectSlotsByName更適合的任務是與desinger配合完成缺省的信號和槽的鏈接。 
其餘:

在測試過程當中,曾經把ui->setupUi(this);放到了控件建立以前運行,結果運行時提示:

QMetaObject::connectSlotsByName: No matching signal for on_TestButton_clicked

從connectSlotsByName的代碼能夠看到這實際上執行的是第46行,若是在調試程序中遇到這樣的信息,能夠檢查一下,是不是控件的objectName與你編寫的槽裏的objectName並不相符。
ui

相關文章
相關標籤/搜索