解读QT信号与槽机制里 QMetaObject::connectSlotsByName(QObject *o)的源码

简介:

介绍


connectSlotsByName 是一个QMetaObject类里的static函数,其定义如下:
static void connectSlotsByName(QObject *o);
其作用是如其名称一样,用来将QObject *o里的子孙QObject的某些信号按照其objectName连接到o的槽上。


起因


为啥会对这个函数产生一探究竟的想法呢?——
既然是根据objectName来连接信号和槽,那么就有了几个问题:
能不能对多个QObject设置同样的objectName呢?
如果能,那么connectSlotsByName会连接多少个QObject的信号到指定的槽上呢?


测试结果


有了疑问,第一个应该做的事情,当然是编写代码进行测试了。
在测试的主窗口类构造函数在“ui->setupUi(this); ”语句前编写如下代码:
    for(int i=0;i<9;++i)
    {
        QPushButton *btn=new QPushButton(this);
        btn->setObjectName(“TestButton”);
        qDebug(btn->objectName().toStdString().c_str());
    }

    ui->setupUi(this);

QMetaObject::connectSlotsByName()这个函数会在ui->setupUi(this);里被调用执行。
然后在主窗口里增加下面的槽定义很代码:
    void on_TestButton_clicked(bool bVal);

    void MainWindow::on_TestButton_clicked(bool bVal)
    {
        QObject *obj=sender();
        qDebug("TestButton is clicked by %s!%d\n",obj->objectName().toStdString().c_str(),bVal);
    }
然后编译运行,结果出来了:
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并不相符。

 

本文转自feisky博客园博客,原文链接:http://www.cnblogs.com/feisky/archive/2010/04/08/1707427.html,如需转载请自行联系原作者


相关文章
|
12天前
|
编解码 开发工具 UED
QT Widgets模块源码解析与实践
【9月更文挑战第20天】Qt Widgets 模块是 Qt 开发中至关重要的部分,提供了丰富的 GUI 组件,如按钮、文本框等,并支持布局管理、事件处理和窗口管理。这些组件基于信号与槽机制,实现灵活交互。通过对源码的解析及实践应用,可深入了解其类结构、布局管理和事件处理机制,掌握创建复杂 UI 界面的方法,提升开发效率和用户体验。
56 12
|
5天前
|
Windows
QT源码拾贝6-11(qwindowswindow)
这篇文章深入探讨了Qt源码中与窗口激活相关的函数,QDebug运算符重载,vscode的变量提示,Windows常用类型名,获取所有窗体的方法,以及QSharedPointer智能指针的使用。
QT源码拾贝6-11(qwindowswindow)
|
5天前
|
存储 Java C++
QT源码拾贝0-5(qimage和qpainter)
这篇文章介绍了在Qt源码中qimage和qpainter的使用,包括线程池的使用、智能指针的存储、std::exchange函数的应用、获取类对象的方法以及QChar字节操作。
QT源码拾贝0-5(qimage和qpainter)
|
2月前
|
存储 C++
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
|
2月前
|
程序员 C++
【Qt】信号与槽(下)
【Qt】信号与槽(下)
|
2月前
|
存储 算法 C++
【C++】C++ QT实现Huffman编码器与解码器(源码+课程论文+文件)【独一无二】
【C++】C++ QT实现Huffman编码器与解码器(源码+课程论文+文件)【独一无二】
|
2月前
|
Linux C++
【Qt】信号与槽(上)
【Qt】信号与槽(上)
【Qt】信号与槽(上)
|
2月前
从源码角度分析Qt元对象系统2
从源码角度分析Qt元对象系统
44 0
|
2月前
|
存储
从源码角度分析Qt元对象系统1
从源码角度分析Qt元对象系统
57 0
|
2月前
【qt】有点意思的信号与槽
【qt】有点意思的信号与槽
15 0
下一篇
无影云桌面