Qt的Signals 和 Slots详解

Qt::AutoConnection的signal-slot连接是在运行时确定连接类型,多线程安全的。

Qt中的关键字:signals 其实就是public;而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来添加自己的槽函数,来响应感兴趣的信号。


信号与槽:对象间通信

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

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

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

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

信号

信号是 public 类型的,可以从任何地方发射。但是推荐在定义信号的类内部发射(signals are public access functions and can be emitted from any where, but we recommend to only emit them from the class that defines the signals and its subclasses)。

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

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

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

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

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

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

槽函数可以通过 信号-槽 的连接而被任何组件调用,这与槽的访问权限无关。这意味着private 的槽也可以被信号调用。(However, as slots, they can be invoked by any component, regardless of its access level, via a signal-slot connection.This means that a signal emitted from an instance of an arbitrary class can cause a private slot to be invoked in an instance of an unrelated class)

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

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

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

例子

#include <QObject>

  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多线程深入分析






最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 为什么在头文件中有的是使用前置声明,而有的是包含头文件? 如下代码: 前置声明(forward declarati...
    Joe_HUST阅读 1,450评论 0 6
  • 信号和槽(Signals and Slots) Qt库第一个认识到在几乎所有情况下,程序员不需要或甚至不想知道所有...
    珞珈村下山阅读 10,152评论 0 23
  • 1、概述 信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检...
    你的社交帐号昵阅读 45,858评论 0 9
  • 韩元旭、余橙、沈开洋 Qt介绍 Qt是一个跨平台的C++图形用户界面应用程序框架。它早在1991年奇趣科技公司两位...
    开洋_shen阅读 16,432评论 4 24
  • 转自:作者简介作者:唐新华 (xhsmart@263.net)软件工程师    信号和槽作为QT的核心机制在QT编...
    njukay阅读 1,417评论 0 49

友情链接更多精彩内容