在測試的主窗口類構造函數在「ui->setupUi(this); 」語句前編寫以下代碼:php
QMetaObject::connectSlotsByName()這個函數會在ui->setupUi(this);裏被調用執行。
而後在主窗口裏增長下面的槽定義很代碼:
html
而後編譯運行,結果出來了:
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