深入了解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)      ”″#a
# define SIGNAL(a) ”″#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 : ; // 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,,);
} void ZMytestObj::sigBtnClicked()
{
  QMetaObject::activate(this,&staticMetaObject,,);
}

每一个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了。

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

  1. 【golang-GUI开发】qt之signal和slot(二)

    上一篇文章里我们详细介绍了signal的用法. 今天我们将介绍slot的使用.在qt中slot和signal十分相像,这次我们将实现一个能显示16进制数字的SpinBox,它继承自QSpinbox并重 ...

  2. 【golang-GUI开发】qt之signal和slot(一)

    想了很久,我决定还是先从signal和slot(信号槽)开始讲起. signal和slot大家一定不陌生,先看一段示例(选自文档): class Counter : public QObject { ...

  3. Qt Signal and Slot

    Qt4中的信号槽 Qt4中的信号槽是通过SIGNAL,SLOT两个宏,将参数转换成字符串.Qt编译前,会从源码的头文件中提取由signal和slot声明的信号和槽的函数, 将其组成一张信号和槽对应的字 ...

  4. qt的signal和slot机制

    signal和slot是QT中的一大特点 signal/slot是Qt对象以及其派生类对象之间的一种高效通信接口 用户可以将N多个信号和单个槽相连接, 或者将将N个槽和单个信号连接, 甚至是一个信号和 ...

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

    学到点什么, 而不是复制着什么, 每天敲着别人给的代码,苦涩得改完bug, 就这样一天天的过去, 实质上并没有学到什么, 别人的思想只是拿来借鉴, 你的思想是好是坏都是你的, 不用急着抛弃自己. 从q ...

  6. PyQt5 signal and slot

    PyQt5 的 signal 与 slot 有所改变,例如,先定义一个 ZeroSignal 类: class ZeroSignal(QObject): atzero = pyqtSignal(int ...

  7. [C++_QT] Error: Not a signal or slot declaration

    问题: 在Qt工程中添加了一个新的窗口之后 一直报错 如下 单单从错误描述上看 是缺少信号或者槽 但是我确定没有缺少啊 然后第二个错误显示了一个mox_xxxx文件 然后我就去那个目录下去找那个文件 ...

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

    编程语言的基元类型   某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32();  // a = 0 a = 1 ...

  9. 【QT】error: macro "SIGNAL" passed 3 arguments, but takes just 1

    error: macro "SIGNAL" passed 3 arguments, but takes just 1 错误原因: "SIGNAL"后面括号掉了. ...

随机推荐

  1. Hbase的安装(hadoop-2.6.0,hbase1.0)

    Hbase的安装相对很简单啊...只要你装了Hadoop 装Hbase就是分分钟的事 如果要装hadoop集群的话 hadoop分类的集群安装好了,如果已经装好单机版~ 那就再配置如下就好~ 一.vi ...

  2. GL_GL系列 - 多币种管理分析(案例)

    2014-07-08 Created By BaoXinjian

  3. c++学习-特殊类成员

    静态变量: #include<iostream> #include<string> #include <typeinfo> using namespace std; ...

  4. linux 命令(1)screen

    一.screen的安装和用法 Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器,这意味着你能够使用一个单一的终端窗口运行多终端的应用. Screen中有会话的概念,用户可以在一个s ...

  5. [物理学与PDEs]第3章 磁流体力学

    [物理学与PDEs]第3章第1节 等离子体 [物理学与PDEs]第3章第2节 磁流体力学方程组 2.1 考虑到导电媒质 (等离子体) 的运动对 Maxwell 方程组的修正 [物理学与PDEs]第3章 ...

  6. file_get_content和curl的性能比较

    今天在获取微信一张二维码图片时发现使用php中的file_get_content方式和curl方式竟然相差了50倍左右,直接晕倒!!!

  7. 树莓派:使用OpenCV调用自带的摄像头.

    总所周知,树莓派上,调用摄像头的指令有raspistill和raspivid.若要使用opencv对摄像头进行调用,不少人会出现 cvCaptureFromCAM(0)函数无法找到Pi Cam的错误情 ...

  8. Yii2.0 数据库查询方法

    User::find()->all();    此方法返回所有数据:    User::findOne($id);   此方法返回 主键 id=1  的一条数据(举个例子):    User:: ...

  9. rand()和srand()区别

    标准库<cstdlib>提供两个帮助生成伪随机数的函数: 函数一:int rand(void):从srand (seed)中指定的seed开始,返回一个[seed, RAND_MAX(0x ...

  10. angularJs中的隐藏和显示

    <!DOCTYPE html> <html ng-app="a2_12"> <head> <meta charset="utf- ...