zoukankan      html  css  js  c++  java
  • 解读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并不相符。

    转自:http://www.cnblogs.com/ttylikl/archive/0001/01/01/1522770.html

  • 相关阅读:
    HDU 2639 Bone Collector II (01背包,第k解)
    POJ 2184 Cow Exhibition 奶牛展(01背包,变形)
    hihoCoder #1165 : 益智游戏 (挑战赛11 B题)
    UVA 562 Dividing coins 分硬币(01背包,简单变形)
    POJ Charm Bracelet 挑饰品 (常规01背包)
    hiho一下 第四十四周 博弈游戏·Nim游戏(直接公式解)
    UVA 624 CD(01背包,要记录路径)
    118 Pascal's Triangle 帕斯卡三角形 杨辉三角形
    117 Populating Next Right Pointers in Each Node II 每个节点的右向指针 II
    116 Populating Next Right Pointers in Each Node 每个节点的右向指针
  • 原文地址:https://www.cnblogs.com/feisky/p/1707427.html
Copyright © 2011-2022 走看看