《《总结之一》
===================================================================================================
查看Qt4的一些示例项目的时候,使用设计器打开其UI文件,在文件中竟然找不到signal和slot的连接。但是最终的程序,slot却又能准确的响应信号。打开通过ui文件自动生成的c++文件,其中也找不到connect语句,到底是怎么一回事?
经过逐语句的分析。终于发现连接的原因就在于setUi函数的最后一句 QMetaObject::connectSlotsByName(MainWindow); 找到该静态函数 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); //以下一行用来判断slot的前三位是否是on_,如果不是,就跳过这个方法。 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(); //要求slot跳过前3位(on_)后,接下来的子字符串和子对象名相同,并且接着该子字符串又是一个_ //如果达不到这个要求,continue if (!len || qstrncmp(slot + 3, objName.data(), len) || slot[len+3] != '_') continue; const QMetaObject *smo = co->metaObject(); int sigIndex = smo->indexOfMethod(slot + len + 4); if (sigIndex < 0) { // search for compatible signals int slotlen = qstrlen(slot + len + 4) - 1; //搜索该子对象所能引发的信号 for (int k = 0; k < co->metaObject()->methodCount(); ++k) { //方法类型如果符合要求 if (smo->method(k).methodType() != QMetaMethod::Signal) continue; //如果slot最后的子字符串和信号名相同 if (!qstrncmp(smo->method(k).signature(), slot + len + 4, slotlen)) { sigIndex = k; break; } } } if (sigIndex < 0) continue; //连接操作 if (QMetaObject::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); } } } 得出此结论:自动生成的文件中,该函数总会存在setUi函数的最后一句。 该函数的作用就是寻找setUi的唯一指针参数MainWindow所指向对象的成员函数, 该成员函数的名字如果满足以下条件,就做连接操作。 函数名规则:on_子对象名_信号名 函数签名(即返回值与参数要符合slot要求) 所以,我们可以这样做:在qt设计器中添加按纽或者菜单项或者按纽项后,不用在设计器中手动做连接操作。 我们只要在主窗口类中添加符合条件的成员函数即可。 函数名规则:on_子对象名_信号名 函数签名(即返回值与参数要符合slot要求) 例如: 在设计器中添加一个菜单项,其对应的action为actionNew 那么在主窗口类中添加以下的函数 public slots: void on_actionNew_triggered(); 当切换这个菜单时,会自动执行上面的成员函数。《《总结之二》
===================================================================================================
别人代码看到void on_MyWidget_slotTest();
就郁闷了,没看到他代码里有connect 却能把信号和槽可以连接起来。
今日回顾书本发现该函所的nb之处。
QMetaObject::connectSlotsByName(QObject * object)将递归的搜寻传入的Qt对象object的所有子对象,并把所有匹配的子对象的信号关联到object对象的符合下列规则的槽函数void on_<窗口部件名称>_<信号名称>(<信号参数>)
如果窗口部件已经提供信号Qt可以自动关联。
《《总结之三》
===================================================================================================
void QMetaObject::connectSlotsByName(QObject *o)
{ if (!o) return; //如果空,退出 const QMetaObject *mo = o->metaObject(); Q_ASSERT(mo); //如果不是一个meta Object const QObjectList list = qFindChildren<QObject *>(o, QString()); //查找子对象,放入list for (int i = 0; i < mo->methodCount(); ++i) { //对于mo的所有方法 const char *slot = mo->method(i).signature(); //取出slot 的名字字符串 Q_ASSERT(slot); if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_') continue; // 如果不是以on_开头,就下次继续 bool foundIt = false; //初始化foundIt 为假, 这是找到标志 for(int j = 0; j < list.count(); ++j) { //对所有的子对象,去找它的signal 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; // 如果objName对不上on_后面的那个字符串的话,说明找错了子对象,继续下次 const QMetaObject *smo = co->metaObject(); int sigIndex = smo->indexOfMethod(slot + len + 4); //找到子对象的signal偏移 if (sigIndex < 0) { // search for compatible signals int slotlen = qstrlen(slot + len + 4) - 1; for (int k = 0; k < co->metaObject()->methodCount(); ++k) { if (smo->method(k).methodType() != QMetaMethod::Signal) continue;if (!qstrncmp(smo->method(k).signature(), slot + len + 4, slotlen)) {
sigIndex = k; break; } } } if (sigIndex < 0) continue; if (QMetaObject::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); } }}
经过我几个小时的艰苦探索,哈哈..终于搞定了,现在给大家分享一下吧...
关键的关键是对象的 "父子关系" !! 必须让发出signal的对象是slot类的对象的儿子 !这个很简单,只要先 m->setParent(this); // 其中,m是发出signal的对象,this是有slot的对象..最后不要忘了, QMetaObject::connectSlotsByName(this); 哦,呵呵 一切就这么简单,开始享受自动连接的快乐吧!!^_^ 写个相应的on_xxxx_xxxx就可以了,再也不要一大堆的connect了...哈哈,,再见啦!