• QMetaObject::connectSlotsByName 总结


    《《总结之一》

    ===================================================================================================

    查看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了...哈哈,,再见啦!

     
  • 相关阅读:
    案例分享:只因在 update 语句中误用一个双引号,生产数据竟然都变成了 0
    快速了解Service Mesh微服务架构实现服务间gRPC通信
    实战|如何优雅地自定义Prometheus监控指标
    微服务架构中如何快速构建一个数据报告服务?
    k8s微服务接入SkyWalking,三分钟教你怎么玩!
    Python基础-27-面向对象
    Python基础-21-字典
    Jmeter JSON提取器
    Jmeter正则表达式提取器
    访问github,修改host文件
  • 原文地址:https://www.cnblogs.com/alleyonline/p/4908438.html
Copyright © 2020-2023  润新知