Qt的Signals 和 Slots详解

Signals 和 Slots 用于对象间的通信(communication between objects)。这种机制是Qt区别于其他框架的主要特点。这种机制是靠Qt的meta-object system实现的。

介绍

很多框架使用callback技术(MFC,CVI等)。一个 callback 其实就是一个函数指针,但是Qt认为callback并不直观,而且在callback参数上容易出问题。

信号与槽

在Qt中使用另一种方案:信号与槽。信号在特定事件发生时被发射。Qt widgets有很多预定义的信号。我们可以子类化widgets来添加自己的信号。槽函数在所连接信号发射后被调用,作为事件的响应。    Qt widgets有很多预定义的槽。实际中我们经常子类化widget来添加自己的槽函数,来响应感兴趣的信号。


Qt的Signals 和 Slots详解_第1张图片
信号与槽:对象间通信

信号与槽机制是类型安全的:信号的签名必须和槽一致(实际槽的签名可以比信号更短,忽略部分参数)。由于签名兼容,编译器可以帮助我们检查类型是否匹配。基于字符串的 SIGNAL 和 SLOT 语法可以在运行阶段检查类型匹配。

所有继承自QObject的类都可以包含信号和槽。槽函数既可以用来接收信号,也可以当做普通函数使用。

信号和槽可以是一对多、多对一。也可以把一个信号连接到另一个信号(这样第一个信号发射后会接着发射第二个信号)。callback技术(即函数指针)只能是一对一。

信号/槽非常类似于C#中事件(event)的发布和订阅。

信号

信号是 public 类型的,可以从任何地方发射。但是推荐在定义信号的类内部发射。

Qt中的关键字:signals 其实就是public;而slots则什么都没有。

(信号非常类似C#中的事件event,可以被订阅)

当信号发射时,连接的槽函数通常立即执行(direct connection),就像普通的函数调用。此时信号和槽机制与GUI的事件循环是独立的。emit 之后的代码在所有槽函数返回后才会被执行(正常,此时的信号-槽,就是函数指针-函数)。这和 queued connections 不同,后者是立即执行 emit 之后的代码,而槽函数在晚些时候执行。

如果多个槽连接到一个信号,当信号发射时,槽函数的执行顺序就是连接的顺序。

信号由moc自动生成,不可以在.cpp文件中实现,而且也不能有返回类型(即只能使用void)。

槽函数就是普通的C++函数。唯一特别的地方是:可以连接到信号。

槽函数是普通的成员函数。也可以定义成virtual类型,非常有用。

信号-槽机制比callback机制的速度略慢,这是增加了灵活性的代价,但是差异并不大。通常,发射一个信号来调用槽函数,通常比直接调用函数慢10倍(10 times slower tha calling the receivers directly),时间主要用来确定接收对象,来安全的遍历连接(例如检查后续的信号接收者没有被销毁),来封送参数。

信号-槽机制消耗的时间比 new 和delete所消耗的时间更短。考虑到它的灵活性,这些实现消耗是值得的。

例子

#include

  class Counter : public QObject

  {

      Q_OBJECT

  public:

      Counter() { m_value = 0; }

      int value() const { return m_value; }

  public slots:

      void setValue(int value);

  signals:

      void valueChanged(int newValue);

  private:

      int m_value;

  };

包含信号和槽的类必须满足两个条件:

1. 在声明的最顶部使用 Q_OBJECT;

2.直接或间接继承自QObject。

void Counter::setValue(int value)

  {

      if (value != m_value) {

          m_value = value;

          emit valueChanged(value);

      }

  }

Counter a, b;      QObject::connect(&a, &Counter::valueChanged,                      &b, &Counter::setValue); 

a.setValue(12);    // a.value() == 12, b.value() == 12

    b.setValue(48);    // a.value() == 12, b.value() == 48

信号和槽的连接类型

Qt::AutoConnection

默认连接方式。当receiver存在于发射信号的线程里时(线程亲和性),使用的是Qt::DirectConnection. 多线程时则使用Qt::QueuedConnnection。连接类型在信号发射时确定(运行时,而非编译时)。

Qt::DirectConnection

信号发射时立即调用槽函数。槽在信号发射的线程里执行。

Qt::QueuedConnnection

当控制权回到receiver所在线程时才执行槽函数。槽函数在receiver的线程里执行。

Qt::BlockingQueuedConnection

和QueuedConnection类似。但是发射信号的线程会阻塞,直到槽函数返回。当receiver和sender在同一个线程里时,不可以使用该方式,否则会发生死锁。

Qt::UniqueConnection

该类型可以和上面的类型配合,用或“|”处理即可。当连接已经存在时,再次连接会失败。其实就是为了保证连接的唯一性。

注意:当使用QueuedConnection时,参数类型必须是 Qt 的 meta-object system 知道的类型,因为Qt要拷贝参数。可以connect()前调用qRegisterMetaType()来注册数据类型。

关于信号和槽在多线程中的使用,参考“QObjects和多线程”。



参考Qt官方文档:"Signals & Slots".

QT多线程深入分析






你可能感兴趣的:(Qt的Signals 和 Slots详解)