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

相关文章
相关标签/搜索