以前经常使用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(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进行强制制定。