以前經常使用qt creator界面管理直接跳轉到槽函數沒發現這個問題,今天手動寫了個槽函數發現按鍵會觸發兩次。根據網上的說法是按鍵會自動連接一個槽函數,如果我們手動添加的槽函數命名規則符合槽函數的命名規則的話,此時就會出現觸發兩次的問題。
帶着這個疑問,我們去代碼里找答案。首先是在界面自動生成的ui_mainwindow.h(由你按鍵所在的界面決定),我們找到了自動建立槽函數連接的代碼
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
繼續跟蹤發現這個系統函數connectSlotsByName是定義在qobject中,切換過去發現文件頭有如下聲明
\section1 Auto-Connection
Qt's meta-object system provides a mechanism to automatically connect
signals and slots between QObject subclasses and their children. As long
as objects are defined with suitable object names, and slots follow a
simple naming convention, this connection can be performed at run-time
by the QMetaObject::connectSlotsByName() function.
大意就是我們之前說的如果槽函數的命名滿足一定形式的簡單規則,那么這個函數就會將信號和槽自動連接起來,跟蹤源代碼我們看下規則到底是怎樣的
void QMetaObject::connectSlotsByName(QObject *o){
if (!o)
return;
const QMetaObject *mo = o->metaObject();
Q_ASSERT(mo);
const QObjectList list = qFindChildren<QObject *>(o, QString());
for (int i = 0; i < mo->methodCount(); ++i) {
const char *slot = mo->method(i).signature();
Q_ASSERT(slot);
if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_')
continue;
bool foundIt = false;
for(int j = 0; j < list.count(); ++j) {
const QObject *co = list.at(j);
QByteArray objName = co->objectName().toAscii();
int len = objName.length();
if (!len || qstrncmp(slot + 3, objName.data(), len) || slot[len+3] != '_')
continue;
int sigIndex = co->d_func()->signalIndex(slot + len + 4);
if (sigIndex < 0) { // search for compatible signals
const QMetaObject *smo = co->metaObject();
int slotlen = qstrlen(slot + len + 4) - 1;
for (int k = 0; k < co->metaObject()->methodCount(); ++k) {
QMetaMethod method = smo->method(k);
if (method.methodType() != QMetaMethod::Signal)
continue;
if (!qstrncmp(method.signature(), slot + len + 4, slotlen)) {
int signalOffset, methodOffset;
computeOffsets(method.enclosingMetaObject(), &signalOffset, &methodOffset);
sigIndex = k + - methodOffset + signalOffset;
break;
}
}
}
if (sigIndex < 0)
continue;
if (QMetaObjectPrivate::connect(co, sigIndex, o, i)) {
foundIt = true;
break;
}
}
if (foundIt) {
// we found our slot, now skip all overloads
while (mo->method(i + 1).attributes() & QMetaMethod::Cloned)
++i;
} else if (!(mo->method(i).attributes() & QMetaMethod::Cloned)) {
qWarning("QMetaObject::connectSlotsByName: No matching signal for %s", slot);
}
}
}
大概看了下(有興趣的可自行研究),要滿足如圖上紅色標志處的如下兩個規則
1、首先是要以on_開頭
2、其次,要以“_”+行為結束,例如_clicked
了解這個后我們再回到之前的問題,假如一個按鍵QPushButton,如果我們定義一個槽函數on_QPushButton_clicked(void)的話,此時不需要額外進行connect()操作,程序編譯時會自動將槽函數連接起來。
而這次如果我們額外進行connect操作的話,就會出現槽函數觸發兩次的情況了。
解決的辦法很簡單,手動連接時槽函數名字規避命名規則就行了,如上我們定義成QPushButton_clicked(void),或者在connect時使用qt::uniqueconnection進行強制制定。
