深入了解Qt(三)之元signal和slot

深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解。在此向原作者表示感谢!

Qt 信号和槽函数这篇文章中已经详细地介绍了信号和槽的使用及注意事项。在这里对其使用方面的知识就不再做介绍,只做一些相应补充。

我们知道信号和槽是用来在对象间通信的一种机制,当一个特定的事件发生时,signal会被emit,slot调用时用来响应响应的signal。signal和slot机制是类型安全的,signal和slot必须互相匹配(实际上一个slot参数可以比对应的signal的参数少,因为它可忽略多余的参数)。signal和slot是松散的配对关系,发出signal的对象不关系是哪个对象执行slot函数,也不关心有多少个slot链接了当前的signal。signal不需要我们实现,它会有moc自动生成,并且它永远不能有返回值。

信号和槽的关系如下图所示:

例如:connect(ui->pushBtn,SIGNAL(clicked()),ui->lineEdit,SLOT(clear()));

clicked()是QPushButton的信号,clear()是QLineEdit的方法。

我们都知道,把一个signal和slot连接起来,需要使用QObject类的connect方法,它的作用就是把一个object的signal和另外一个object的slot连接起来,以达到对象间通讯的目的。

connect 在幕后到底都做了些什么事情?为什么emit一个signal后,相应的slot都会被调用?

SIGNAL和SLOT宏定义

connect(&obj, SIGNAL(destroyed()), &app, SLOT(aboutQt()));

在这里signal和slot的名字都被包含在了两个大写的SIGNAL和SLOT中,这两个是什么呢?原来SIGNAL 和 SLOT 是Qt定义的两个宏。

# define SLOT(a)      ”1″#a
# define SIGNAL(a)   ”2″#a  

Qt把signal和slot都转化成了字符串,并且还在这个字符串的前面加上了附加的符号,signal前面加了’2’,slot前面加了’1’。也就是说,我们前面写了下面的connect调用,在经过moc编译器转换之后,就便成了:
connect(&obj, “2destroyed()”, &app,
“1aboutQt()”)); 当connect函数被调用了之后,都会去检查这两个参数是否是使用这两个宏正确的转换而来的,它检查的根据就是这两个前置数字,是否等于1或者是2,如果不是,connect函数当然就会失败啦!

然后,会去检查发送signal的对象是否有这个signal,方法就是查找这个对象的class所对应的staticMetaObject对象中所包含的d.stringdata所指向的字符串中是否包含这个signal的名字,在这个检查过程中,就会用到d.data所指向的那一串整数,通过这些整数值来计算每一个具体字符串的起始地址。同理,还会使用同样的方法去检查slot,看响应这个signal的对象是否包含有相应的slot。这两个检查的任何一个如果失败的话,connect函数就失败了,返回false。

前面的步骤都是在做一些必要的检查工作,下一步,就是要把发送signal的对象和响应signal的对象关联起来。在QObject的私有数据类QObjectPrivate中,有下面这些数据结构来保存这些信息:

class QObjectPrivate : public QObjectData
{
  struct Connection
  {
    QObject *receiver;
    int method;
    uint connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
    QBasicAtomicPointer<int> argumentTypes;
  };  

  typedef QList<Connection>; ConnectionList;
  QObjectConnectionListVector *connectionLists;  

  struct Sender
  {
    QObject *sender;
    int signal;
    int ref;
  };  

  QList<Sender> senders;
};  

在发送signal的对象中,每一个signal和slot的connection,都会创建一个QObjectPrivate::Connection对象,并且把这个对象保存到connectionList这个Vector里面去。

在响应signal的对象中,同样,也是每一个signal和slot的connection,都会一个创建一个Sender对象,并且把这个对象附加在Senders这个列表中。

以上就是connect的过程,其中,创建QObjectPrivate::Connection对象和Sender对象的过程有一点点复杂,需要仔细思考才可以,有兴趣的朋友可以去读一下源代码。

emit幕后的故事

当emit signal的时候,与这个signal相连的slot函数就会被调用,那么这个调用时如何发生的呢?

看段代码:

class ZMytestObj : public QObject
{
  Q_OBJECT
  signals:
  void sigMenuClicked();
  void sigBtnClicked();
};  

moc编译器在做完预处理之后的代码如下:

void ZMytestObj::sigMenuClicked()
{
  QMetaObject::activate(this,&staticMetaObject,0,0);
}

void ZMytestObj::sigBtnClicked()
{
  QMetaObject::activate(this,&staticMetaObject,1,0);
}

每一个signal都会被转换为一个与之相对应的成员函数。也就是说,当我们写下这样一行代码:emit sigBtnClicked();当程序运行到这里的时候,实际上就是调用了void ZMytestObj::sigBtnClicked() 这个函数。

void ZMytestObj::sigMenuClicked()  void ZMytestObj::sigBtnClicked(),它们唯一的区别就是调用 QMetaObject::activate 函数时给出的参数不同,一个是0,一个是1,它们的含义是什么呢?它们表示是这个类中的第几个signal被发送出来了,回头再去看头文件就会发现它们就 是在这个类定义中,signal定义出现的顺序,这个参数可是非常重要的,它直接决定了进入这个函数体之后所发生的事情。

当执行流程进入到QMetaObject::activate函数中后,会先从connectionLists这个变量中取出与这个signal相对应的connection list,它根据的就是刚才所传入进来的signal index。这个connection list中保存了所有和这个signal相链接的slot的信息,每一对connection(即:signal和slot的连接)是这个list中的一项。

在每个一具体的链接记录中,还保存了这个链接的类型,是自动链接类型,还是队列链接类型,或者是阻塞链接类型,不同的类型处理方法还不一样的。

对于直接链接的类型,先找到接收这个signal的对象的指针,然后是处理这个signal的slot的index,已经是否有需要处理的参数,然后就使用这些信息去调用receiver的qt_metcall 方法。在qt_metcall方法中就简单了,根据slot的index,一个大switch语句,调用相应的slot函数就OK了。

时间: 2024-10-18 10:43:06

深入了解Qt(三)之元signal和slot的相关文章

qt学习(一)qt三个文件函数的框架

学到点什么, 而不是复制着什么, 每天敲着别人给的代码,苦涩得改完bug, 就这样一天天的过去, 实质上并没有学到什么, 别人的思想只是拿来借鉴, 你的思想是好是坏都是你的, 不用急着抛弃自己. 从qt看看人家的思路. Qt编程思路: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~` 以下:xxx.h ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

实现类似于Qt的Signal和Slot通讯机制

Signal和Slot机制 其实qt提供的对象间通讯机制. 实现细节 思路 实现类似与信号与槽的机制,无非就是做一些薄记工作,qt中通过虚函数和moc来实现.那我们怎么实现?我的思路是signal是一个对象,其他的slot将自己的回调注册进signal对象中,signal完成薄记工作.那么需要实现的细节归于一下几点. 1. 参数的提取与匹配. 2. slot容器的制作. 3. slot的调用. 参数处理 怎么处理参数是一大难题.我们必须推导出各类型,包括类的类型和参数类型,在此我们引入很多特性模

Qt5的插件机制(4)--Qt插件的元信息metaData

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Signal和slot的声明和连接

信号(SIGNAL)和槽(SLOT)是Qt编程的一个重要部分.这个机制可以在对象之间彼此并不了解的情况下将它们的行为联系起来. Signal和slot的声明(一) 在Qt程序设计中,凡是包含signal和slot的类中都要加上Q_OBJECT的定义,下面的例子给出了如何定义在一个类中定义signal和slot 信号的定义: siganls: void mySignal(); void mySignal( int x ); void mySignal( int x, int y ); 其中sign

Signal和Slot是同步的还是异步的

signal和slot是异步调用还是同步调用,取决于对connect的设定.其实connect还有一个参数(Qt::ConnectionType),是它决定了是同步还是异步.以下是ConnectionType的定义 只不过,平常它有一个默认值Qt::AutoConnection,我们忽略了它.这时有道友问道,为何在AutoConnection模式下,有时是同步,有时是异步,莫非Auto就是人品代名词. 非也,其实Auto是这样规定的, 当sender和receiver在同一线程时,就是同步模式,

CLR via C#深解笔记三 - 基元类型、引用类型和值类型 | 类型和成员基础 | 常量和字段

编程语言的基元类型 某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32();  // a = 0 a = 1; 等价于: int a = 1; 这种语法不仅增强了代码的可读性,其生成的IL代码与使用System.Int32时生成的IL代码是完全一致的. 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到Framework类库(FCL)中存在的类型.如C#中,int直接映射

iOS快速开发框架Bee-Framework应用和解析(三) --- Message, Model, Signal

这一次分享一下BeeMessage, BeeModel, 和BeeUISignal.这三个东东就是Controller, Model, 和Event的主要实现.您也可以到Bee的/documents/developer_manual.pdf中查看详细的开发手册,希望您看了这篇文章能对这几个组件理解更深,适合干什么,从而更得心应手得使用.本文试图解答几个问题: BeeMessage是如何实现的?如何做到封装Http过程的,有什么优缺点和适用场合? BeeModel是如何实现的?和Core Data

python 第三章 元祖,字典,集合,while循环

元祖 Python的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 tup = (1,2,3,4,"hello","word") 5 print(type(tup)) 创建空元组 1 tup = () 元组中只包含一个元素时,需要在元素后面添加逗号 1 tup =

三丰元:免费云服务器部署web项目

最近在准备毕业答辩,写了一个Web项目,导师要我最好可用通过外网访问.最开始想到Ali云看来以下,但是经费有限.然后通过朋友推荐找到三 丰 云,提供免费得云平台,申请速度也是相当得快速. 三 丰 云 地 址:https://www.sanfengyun.com/ 原文地址:https://www.cnblogs.com/luomeng/p/10888093.html