Signal and Slots

Signal and Slots 用于对象之间通信。 它是 Qt 的核心特性之一, 并且也是Qt 与其它框架差别最大的部分。

概述

在GUI编程中, 如果我们改变了一个控件, 我们可能想其它控件知道; 换言之, 我们希望任何类型的 Object 能够彼此通信。

一些Tookits使用回调函数来实现通信,如果你想在一个函数中通告一些事件, 你需要在这个函数中使用一个函数指针。

但是这种做法有两个缺陷:

  • 他们不是类型安全的, 我们在编译期无法获知这个指针是否正确。
  • 这个回调函数把调用者和被调用者仅仅结合在了一起。

在Qt中, 我们有回调函数的替代办法, 使用Signal and Slots。 一个Signal 会在某个事件发生时被发射, 而slot 是一个用于响应特定signal的函数。 Qt 有很多预定义的信号 、槽, 但是我们可以通过定义 Object 的子类来加入自己的信号、槽。

  • 类型安全: signal 和 slot 的签名(名称、参数)必须一致; 不过事实上,slot的参数是取signal的前n个(n表示signal的参数个数), 因为slot可以忽略信号附带的一些值。 因为这种签名机制兼容于 C++, 因此编译器可以发现类型错误。
  • 发射信号的类不关注是谁接收了这个信号。

当一个Object发生变化、并且它希望这种变化为外界所知道时, 它会发出信号;但它不会理会最终是谁处理了这个信号。 这就是信息封装, 它使得 Qt Object 能够像软件组件那样被使用。

一个信号可以连接多个槽; 多个信号可以连接单个槽; 信号和信号也可以直接连接。

编译

C++ 预处理器修改、删除signal  slot和 emit 关键字, 为 C++编译器提供了符合 标准 C++语法的代码。

在包含signal slot的类定义中运行moc, 一个代码源文件会产生。 它能够使用工程中的其他 object 文件进行编译和链接, 也能被其它Object文件如此使用。如果使用qmake, 这个规则会在qmake 产生的makefile中自动添加。

Signal

信号能够在任何地方发射, 但是最好在定义它的类及子类里使用。

当一个信号发出后, slot函数会立即执行, 跟一个函数调用表现相同; 此时, 这个signal-slot 是独立于event system的。 但是如果在connect时使用 Queued Connection, emit之后的代码会继续执行。 而slot会在稍后的事件循环中被调用。

如果一个信号连接多个槽, 那么这些槽函数会按照它们被连接的顺序依次执行。

如果在信号槽中使用了特殊类型, 则该信号或者槽很难与其它类型通信。 比如 QScrollBar::valueChanged()  使用了 QScrollBar::Range 作为参数, 则它只可能与QScrollBar的槽函数连接。

在一些第三方库中也会有signal /slots 关键字, 这可能会导致编译器发生 warning  或者 error。 可以使用 #undef 去掉某个 signal / slots 的定义。

Slots

除了能够在其连接的signal发射时被调用, slot 是正常的 C++成员函数。 因此, 它们在被调用时, 跟一般成员函数表现一致。 而作为 slot, 无论它的访问级别(protected, private, public), 它们能够被任意Object 调用。 也就是说, 一个任意Object的信号能够引发某一类的private slots。

同时, virtual 类型的slot在实际中也很有用。

和callback 相比, Signal and Slots 提供的灵活性也会稍微有些慢,当然这个在实际应用中是无关紧要的。 一般来说, 在没有虚函数调用的前提下, 从发出一个信号, 到槽函数调用,会比直接调用槽函数慢 10倍。 这些开销其中包括 定位连接的类, 安全的遍历所有连接(i.e. checking that subsequent receivers have not been destroyed during the emission) , 按照通用的模式准备参数。 尽管10个函数调用才及的上一个 signal-slot, 它还是比 new 和 delete 的开销要小得多。 整体开来, 它在所有的函数调用导致的开销中所占比重非常小。系统调用也导致比 signal-slot大的多的开销, 它甚至有可能直接导致多余10个的函数调用。

在一台 i586-500 机子上, 你可以对一个 receiver 发出 2,000,000 个信号, 或者对两个receiver 发出 1,200,000 个信号。

Meta Object In Signal/Slots

Meta Object Compiler 遍历声明类的文件, 并生成初始化 Meta-Object 的文件。 MetaObject 包含了所有的信号槽, 以及指向他们的指针。

MetaObject 中包含了C++ 类的一些额外的信息。比如,

类名;

调用 inherits 查询某个对象的类别所需信息;

使用 qobject_cast 所需信息;

Q_OBJECT 宏会在预处理器中被展开, 并生成几个函数, 这些函数会在moc中生成函数体。 如果发生编译错误“undefined reference to vtable for ClassName”, 你可能忘记运行moc, 或者在链接时没有加上 moc 的输出文件。

Moc 会忽略其它的成员函数(只处理 signal slot)。

对于定义了默认参数值的signal, slot中若有这个参数, 则应该有相同的定义, 或者忽略该参数, 例如 对于QObject::destroyed(QObject * = 0)。

应该是:

connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(Qbject*)));

connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed()));

connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));

高阶用法

如果你需要发送者的信息,你可以调用 QObject::sender(), 获取指向sender 的指针。

QSignalMapper用于”有多个signal 同时连接一个槽, 而这个槽需要对不同的signal 做不同处理”的情况。

例如, 你有3个pushButton用于选择需要打开的文件: "Tax File", "Accounts File", or "Report File",

为了正确的打开文件, 可以使用QSignalMapper::setMapping把click信号映射到QSignalMapper类, 然后把click信号连接到 QSignalMapper 的map槽函数。

signalMapper = new QSignalMapper(this);

signalMapper->setMapping(taxFileButton, QString("taxfile.txt"));

signalMapper->setMapping(accountFileButton, QString("accountsfile.txt"));

signalMapper->setMapping(reportFileButton, QString("reportfile.txt"));

connect(taxFileButton, SIGNAL(clicked()),

signalMapper, SLOT (map()));

connect(accountFileButton, SIGNAL(clicked()),

signalMapper, SLOT (map()));

connect(reportFileButton, SIGNAL(clicked()),

signalMapper, SLOT (map()));

然后再把 mapped 信号连接到处理文件的readFile 槽函数上。

connect(signalMapper, SIGNAL(mapped(QString)),

this, SLOT(readFile(QString)));

以下的代码也会运行, 但是由于 Qt参数归一化操作(signature normalization), 会稍微慢一些。

connect(signalMapper, SIGNAL(mapped(const QString &)),

this, SLOT(readFile(const QString &)));

使用 3rd signal/slot

可以在Qt中使用3rd的 signal/slot机制, 甚至可以在同一个工程中使用。但需要把下面一行代码加入到pro文件中:

CONFIG += no_keywords

它告诉Qt不适用 moc的关键字signals/slots/emit, 因为这些名字会在3rd中出现, 比如boost。 在使用signal/slot的地方, 可以简单的使用 Qt宏 Q_SIGNALS (or Q_SIGNAL), Q_SLOTS (or Q_SLOT), and Q_EMIT 替代。

Comments:

  1. signature normalization:  对于 QObject::connect的 Queued Connection, Qt 会把参数信息保存起来, 以在event system中查询, 这时会调用 参数的拷贝构造函数以及其它信息。 因此参数必须是能够被 Qt‘s meta-object system 识别的类, 如果希望使用自定义类, 应该在connect之前调用qRegisterMetaType(), 把自定义类添加到 Qt‘s meta-object system。

参考 stackoverflow

What benefits does QT get with normalized signature

Qt MOC: When default and copy constructor are used?

时间: 2024-10-24 01:06:15

Signal and Slots的相关文章

Qt中连接到同一signal的多个slots的执行顺序问题(4.6以后按连接顺序执行)

起源 前些天忘记在哪儿讨论过这个问题,今天在csdn又看到有网友问这个问题,而其他网友却无一例外的给出了“无序”这个答案. Manual Qt的问题,当manual中有明确文字说明时,我们应该以Qt的manual为准: http://doc.qt.nokia.com/4.8/signalsandslots.html If several slots are connected to one signal, the slots will be executed one after the othe

关于Qt信号与槽机制的传递方向性研究(结论其实是错误的,但是可以看看分析过程)

最近由于项目的需求,一直在研究Qt.信号与槽机制是Qt的一大特色,该机制允许两者间传递参数,依次来实现对象间的通信.这个参数会分别存在于信号的参数列表和槽函数的参数列表中.需要注意的是,若将槽函数绑定至信号,槽函数的参数列表元素数目只能少于等于信号的参数列表元素数目.而且顺序和类型不能改变.至于缺少的参数应从信号参数尾部开始缺少. 突然今天想起来一个问题,如果一个对象发出信号,将内部的一个成员变量(非简单类型)作为参数向外发送,槽函数就可以接收到这个对象,那么槽函数是否可以完全操作这个对象呢?如

Creating Dialogs

1 #ifndef DIALOG_H 2 #define DIALOG_H 3 4 #include <QtWidgets> 5 //#include <QDialog> 6 //#include <QLabel> 7 //#include <QCheckBox> 8 //#include <QLineEdit> 9 //#include <QPushButton> 10 //#include <QtCore> 11 na

VS2008 Qt Designer 中自定义信号槽

一.Qt Designer自定义槽函数 发现:在VS2008 +Qt4.7  中打开ui文件,所用的英文QT Designer工具,没有转到槽函数的功能,不如QtCreator自带的QtDesigner功能齐全,只能Editor已有的信号槽. 发现:在QtCreator中右击某个控件 有"转到槽"功能,就会在.h和.cpp文件中生成我们的自定义槽函数void on_openButton_clicked() 解决方法:在VS2008中,选择某个项目,选择菜单Qt/Creat basic

Qt MetaObject System详解

网上的资源比较乱,该文章整理自地址:http://www.xuebuyuan.com/735789.html Qt meta-object系统基于三个方面: 1.QObject提供一个基类,方便派生类使用meta-object系统的功能: 2.Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性.信号.槽: 3.Meta Object编译器(MOC),为每个QObject派生类生成代码,以支持meta-object功能 QObject定义了从一个QObject对象访问m

QObject成员函数connect()函数

1:首先要链接的两个类必须继承于QObject,同时添加 Q_OBJECT. 2:在qt中QObject::connect中填写的signal和slot函数,一定要填写参数类型. 因为类中的函数可以,也就是,重载函数名一样,参数不一样,如果QObject::connect中的函数没有参数类型,则无法正确连接. 3:QObject::connect中的signal 和 slot 函数一定要有参数类型, 但是,不可以有参数: You must use the SIGNAL() and SLOT()

Qt Meta Object System-元对象系统

Qt Meta Object System-元对象系统 元对象系统的构成 QObject为所有需要利用元对象系统的对象提供一个基类. Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性.信号和槽. Meta Object Compiler(MOC),为每个QObject派生类生成代码,以支持meta-object功能. QObject定义了从一个QObject对象访问meta-object功能的接口,Q_OBJECT宏用来告诉编译器该类需要激活meta-object功

Qt之UI文件设计和运行机制

1.项目文件组成在QtCreator中新建一个WidgetApplocation项目,选中窗口基类中选中QWidget作为窗口基类,并选中"GnerateForm"复选框.创建后项目文件目录树如图: 项目组织文件pro:存储项目设置的文件主程序入口文件main.cpp,实现main函数的程序文件窗体界面文件widget.ui:一个XML格式存储的窗体上的文件以及其布局的文件widget.h是所设计的窗口类的头文件,widget.cpp是widget.h里定义类的实现文件.在C++里面,

QT 中 关键字讲解(emit,signal,slot)

Qt中的类库有接近一半是从基类QObject上继承下来,信号与反应槽(signals/slot)机制就是用来在QObject类或其子类间通讯的方法.作为一种通用的处理机制,信号与反应槽非常灵活,可以携带任意数量的参数,参数的类型也由用户自定.同时其本身也是类型安全的,任何一个从QObject或其子类继承的用户类都可以使用信号与反应槽. 信号的作用如同Windows系统中的消息.在Qt中,对于发出信号的对象来说,它并不知道是谁接收了这个信号.这样的设计可能在某些地方会有些不便,但却杜绝了紧耦合,于