00. 目录

文章目录

  • 00. 目录
  • 01. 信号与槽
  • 02. 介绍
  • 03. 信号与槽
  • 04. 信号
  • 05. 槽
  • 06. 一个小例子
  • 07. 一个真实的例子
  • 08. 信号和槽使用默认参数
  • 09. 信号与槽高级用法
  • 10. 使用Qt与第三方信号和插槽

01. 信号与槽

​ 信号和槽用于对象之间的通信。信号和插槽机制是Qt的核心功能,可能是与其他框架提供的功能最不同的部分。Qt的元对象系统使信号和槽成为可能。

02. 介绍

​ 在GUI编程中,当我们更改一个小部件时,我们经常需要通知另一个小部件。更一般地说,我们希望任何类型的对象能够彼此通信。例如,如果用户单击“ **关闭”**按钮,我们可能希望调用窗口的close()函数。

​ 其它工具包使用回调实现这种通信。回调是指向函数的指针,因此如果您希望处理函数通知您某些事件,则将指针传递给处理函数的另一个函数(回调)。然后,处理函数在适当时调用回调。虽然确实存在使用此方法的成功框架,但回调可能不直观,并且可能在确保回调参数的类型正确性方面存在问题。

03. 信号与槽

​ 在Qt中,我们有一种替代回调技术:我们使用信号和槽。发生特定事件时会发出信号。Qt的小部件有许多预定义信号,但我们总是可以将部件子类化为了向它们添加我们自己的信号。槽是响应于特定信号而被调用的函数。Qt的部件中有许多预定义的槽函数,但通常的做法是将部件子类化并添加自己的槽,以便您可以处理您感兴趣的信号。

​ 信号和槽机制是类型安全的:信号的签名必须与接收槽的签名匹配。(事实上,一个槽可能比它收到的信号具有更短的签名,因为它可以忽略额外的参数。)由于签名是兼容的,编译器可以帮助我们在使用基于函数指针的语法时检测类型不匹配。基于字符串的SIGNAL和SLOT语法将在运行时检测类型不匹配。信号和槽是松散耦合:发出信号的类既不知道也不用关心哪个槽接收该信号。Qt的信号和槽机制确保如果您将信号连接到槽,将在适当的时间使用信号的参数调用槽。信号和槽可以采用任何类型的任意数量的参数。它们完全是类型安全的。

​ 从QObject或其子类之一(例如,QWidget)继承的所有类都可以包含信号和槽。当它们以某种方式改变它的状态时, 信号就会被发送, 其它对象可能对该信号感兴趣。这是的话所有的对象都可以通讯。它不知道或关心是否有任何东西正在接收它发出的信号。这是真正的信息封装,并确保该对象可以用作软件组件。

​ 槽可用于接收信号,但它们也是普通的成员函数。就像一个对象不知道是否有任何东西接收到它的信号一样,一个槽函数不知道它是否有任何信号连接到它。这确保了可以使用Qt创建真正独立的组件。

​ 可以将任意数量的信号连接到一个槽,并且可以根据需要将信号连接到任意数量的插。甚至可以将信号直接连接到另一个信号。(每当发射第一个信号时,将立即发出第二个信号。)

信号和插共同构成了一个强大的组件编程机制。

04. 信号

​ 当对象的内部状态以某种可能对对象的客户端或所有者感兴趣的方式发生更改时,对象会发出信号。信号是公共访问函数,可以从任何地方发出,但我们建议只从定义信号及其子类的类中发出它们。

​ 当信号发出时,通常会立即执行与其连接的槽函数,就像正常的函数调用一样。发生这种情况时,信号和槽机制完全独立于任何GUI事件循环。emit发出信号之后, 所有与它关联的槽函数将会被执行。这种情形与使用排队连接时情况略有不同; 在这种情况下,emit关键字后面的代码将立即继续,并且稍后将执行槽函数。

​ 如果多个槽连接到一个信号,则在发出信号时,槽函数将按照它们已连接的顺序依次执行。

​ 信号由moc自动生成,不得在.cpp文件中实现。它们永远不会有返回类型(即使用void)。

关于参数的说明

我们的经验表明,如果信号和槽不使用特殊类型,则它们可以重复使用。如果QScrollBar :: valueChanged()使用特殊类型,例如假设的QScrollBar :: Range,则它只能连接到专门为QScrollBar设计的槽函数。将不同的输入控件连接在一起是不可能的。

05. 槽

​ 当连接的信号被发送时, 对应的槽函数将会被执行。槽函数是普通的C ++函数,可以正常调用; 它们唯一的特点是信号可以连接到它们。

​ 由于插槽是普通的成员函数,因此它们在直接调用时遵循正常的C ++规则。但是,作为槽,它们可以通过信号和槽连接由任何组件调用,而不管其访问级别如何。这意味着从任意类的实例发出的信号可以导致在不相关的类的实例中调用私有槽。

​ 我们发现这些槽在实践中非常有用时, 还可以将槽函数定义为虚槽函数。

​ 与回调相比,信号和槽稍微慢一些,因为它们提供了更大的灵活性,尽管实际应用的差异是微不足道的。通常,发射连接到某些槽的信号比使用非虚函数直接调用槽函数大约慢十倍。这是定位连接对象,安全地遍历所有连接(即检查后续槽函数在发射期间没有被销毁)以及以通用方式编组任何参数所需的开销。虽然十个非虚函数调用可能听起来很多,但它的开销比任何newdelete操作都少得多,例如。只要执行场景后面的字符串,vector或list操作,就需要newdelete,信号和槽开销仅仅是函数调用开销的一小部分。无论何时在槽函数中进行系统调用,情况都是如此; 或间接调用十多个函数。由于信号和槽机制的简单性和灵活性, 这一点开销是非常值得的,用户甚至都不会注意到。

​ 请注意,定义变量的其他库在与基于Qt的应用程序一起编译时调用signalsslots可能导致编译器警告和错误。要解决这个问题,#undef违规的预处理器符号。

06. 一个小例子

最小的C ++类声明可能是:

  class Counter{public:Counter() { m_value = 0; }int value() const { return m_value; }void setValue(int value);private:int m_value;};

一个小的基于QObject的类可能是:

#include <QObject>class Counter : public QObject{Q_OBJECTpublic: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;};

​ 基于QObject的版本具有相同的内部状态,并提供访问状态的公共方法,但此外它还支持使用信号和槽进行组件编程。这个类可以通过发出信号告诉外面世界它的状态已经改变了valueChanged(),并且它有一个其它对象可以发送信号的槽。

​ 包含信号或槽的所有类必须在其声明的顶部提及Q_OBJECT。它们还必须(直接或间接)从QObject派生。

​ 槽函数由应用程序员实现。以下是Counter::setValue()槽函数的可能实现:

void Counter::setValue(int value){if (value != m_value) {m_value = value;emit valueChanged(value);}}

​ 该emit行发出valueChanged()来自对象的信号,新值作为参数。

​ 在下面的代码片段中,我们创建了两个Counter对象,并使用QObject :: connect()将第一个对象的valueChanged()信号连接到第二个对象的setValue()槽:

Counter a, b;QObject::connect(&a, &Counter::valueChanged,&b, &Counter::setValue);a.setValue(12);     // a.value() == 12, b.value() == 12b.setValue(48);     // a.value() == 12, b.value() == 48

​ 调用a.setValue(12)导致对象a发出一个valueChanged(12)信号,该信号对象比b将会接收, 并且执行槽函数setValue(),即被b.setValue(12)调用。然后b发射相同valueChanged()的信号,但由于没有槽函数已经被连接到bvalueChanged()`信号,该信号被忽略。

请注意

setValue()函数仅设置值并发出信号value != m_value。这可以防止在循环连接的情况下无限循环(例如,如果b.valueChanged()连接到a.setValue())。

​ 默认情况下,对于您所做的每个连接,都会发出一个信号; 发出两个信号用于重复连接。您可以通过一次disconnect()调用来中断所有这些连接。如果传递Qt :: UniqueConnection 类型,则只有在不是重复的情况下才会建立连接。如果已经有重复(完全相同的信号到相同对象上的完全相同的槽),连接将失败并且connect将返回false

​ 此示例说明对象可以一起工作,而无需了解彼此的任何信息。为了实现这一点,对象只需要被连接在一起,并且这可以用一些简单的实现的QObject ::连接()函数调用,或者与uic的自动连接功能。

07. 一个真实的例子

这是一个小部件的简单评论示例。

#ifndef LCDNUMBER_H#define LCDNUMBER_H#include <QFrame>class LcdNumber : public QFrame{Q_OBJECT

LcdNumber通过QFrame和QWidget继承具有大部分信号槽知识的QObject。它有点类似于内置的QLCDNumber小部件。

​ Q_OBJECT宏由预处理器扩展来声明由moc实现的几个成员函数; 如果您在“未定义的vtable引用”中遇到编译器错误LcdNumber,您可能忘记运行moc或在链接命令中包含moc输出。

  public:LcdNumber(QWidget *parent = 0);

​ 它与moc显然不相关,但是如果你继承了QWidget,你几乎肯定希望parent在构造函数中使用该参数并将其传递给基类的构造函数。

​ 这里省略了一些析构函数和成员函数; 该moc忽略成员函数。

signals:void overflow();

​ 当要求显示不可能的值时LcdNumber发出信号。

​ 如果您不关心溢出,或者您知道不会发生溢出,则可以忽略该overflow()信号,即不要将其连接到任何槽。

​ 另一方面,如果要在数字溢出时调用两个不同的错误函数,只需将信号连接到两个不同的槽即可。Qt将调用它们(按照它们连接的顺序)。

public slots:void display(int num);void display(double num);void display(const QString &str);void setHexMode();void setDecMode();void setOctMode();void setBinMode();void setSmallDecimalPoint(bool point);};#endif

​ 槽是一种接收函数,用于获取有关其他小部件中状态更改的信息。LcdNumber如上面的代码所示,使用它来设置显示的数字。由于display()该类是与该程序其余部分的接口的一部分,因此该槽是公共的。

​ 有几个示例程序将QScrollBar的valueChanged()信号连接到插槽,因此LCDNumber会连续显示滚动条的值。

​ 注意display()重载; 当您将信号连接到槽时,Qt将选择适当的版本。使用回调,您必须找到五个不同的名称并自己跟踪类型。

​ 此示例中省略了一些不相关的成员函数。

08. 信号和槽使用默认参数

信号和槽的签名可以包含参数,参数可以具有默认值。考虑QObject :: destroyed():

  void destroyed(QObject* = 0);

当QObject的被删除时,它发射这个 QObject::destroyed()信号。我们希望捕获这个信号,无论我们对删除的QObject有何悬空引用,我们都可以清理它。合适的插函数可能是:

  void objectDestroyed(QObject* obj = 0);

要将信号连接到槽,我们使用QObject :: connect()。有几种方法可以连接信号和插槽。第一个是使用函数指针:

 connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);

将QObject :: connect()与函数指针一起使用有几个优点。首先,它允许编译器检查信号的参数是否与槽的参数兼容。如果需要,编译器也可以隐式转换参数。

您还可以连接到仿函数或C ++ 11 lambdas:

connect(sender, &QObject::destroyed, [=](){ this->m_objects.remove(sender); });

将信号连接到槽的另一种方法是使用QObject :: connect()和SIGNALSLOT宏。规则是否包含参数在SIGNAL()SLOT()宏中,如果参数有默认值,是传递给签名SIGNAL()宏必须不能比传递到SLOT()宏签名参数少。

所有这些都可行

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

但这一个不起作用:

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

因为槽将期望信号不会发送的QObject。此连接将报告运行时错误。

请注意,使用此QObject :: connect()重载时,编译器不会检查signal和slot参数。

09. 信号与槽高级用法

​ 对于您可能需要有关信号发送者的信息的情况,Qt提供QObject :: sender()函数,该函数返回指向发送信号的对象的指针。

​ 所述QSignalMapper类提供了一种用于有许多信号被连接到相同的槽和槽需要不同的方式处理每一个信号的情况。

​ 假设您有三个按钮,用于确定要打开的文件:“税务文件”,“帐户文件”或“报告文件”。

​ 要打开正确的文件,可以使用QSignalMapper :: setMapping()将所有QPushButton :: clicked()信号映射到QSignalMapper对象。然后将文件的QPushButton :: clicked()信号连接到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, &QPushButton::clicked,signalMapper, &QSignalMapper::map);connect(accountFileButton, &QPushButton::clicked,signalMapper, &QSignalMapper::map);connect(reportFileButton, &QPushButton::clicked,signalMapper, &QSignalMapper::map);

​ 然后,将mapped()信号连接到readFile()将打开其他文件的位置,具体取决于按下的按钮。

      connect(signalMapper, SIGNAL(mapped(QString)),this, SLOT(readFile(QString)));

10. 使用Qt与第三方信号和插槽

可以将Qt与第三方信号/插槽机制一起使用。您甚至可以在同一个项目中使用这两种机制。只需将以下行添加到qmake项目(.pro)文件即可。

CONFIG += no_keywords

它告诉Qt的不要定义moc关键字signalsslotsemit,因为这些名称将由第三方库可以使用,例如boost。然后继续使用带有no_keywords标志的Qt信号和插槽,只需将源中Qt moc关键字的所有使用替换为相应的Qt宏Q_SIGNALS(或Q_SIGNAL),Q_SLOTS(或Q_SLOT)和Q_EMIT。

另请参阅元对象系统和Qt的属性系统。

【Qt】Qt中信号与槽相关推荐

  1. 【Qt专栏】Qt 中信号与槽的概念和实现机制

    目录 一,信号和槽概念 1.元对象系统 2.信号和槽 3.底层实现机制 二,什么是观察者设计模式 三,观察者设计模式的编程套路 四,纯 C++ 实现信号与槽机制 1.槽函数模板类 2.信号模板类 3. ...

  2. Qt for Python 信号和槽的使用详解

    前言 如果要说 Qt 里面的最常听到的词,那么 "信号"(signals)和"槽"(slots)绝对是其中之一了,其功能简单强大,想必每个 Qt 程序员最能理解 ...

  3. Qt eventFilter实现信号与槽

    对于Qt来说,信号与槽机制是其通信的一大亮点,但是Qt中也提供了其他的方法实现数据间的通信,如invokeMethod反射机制实现通信(https://blog.csdn.net/xx18030637 ...

  4. Qt的特性——信号和槽

    Qt的特性--信号和槽 1. Qt的特性 2. 信号和槽 2.1 信号和槽机制概述 2.2 典型应用示例 2.3 信号和槽的关联 2.4 断开关联 2.5 信号和槽的高级应用 1. Qt的特性 Qt在 ...

  5. QT学习之②信号与槽,以及链接实现方法

    QT学习之②信号与槽 ~QQ:3020889729 ~小蔡 信号与槽的意义 信号与槽的实质(文字:举例) 信号与槽的工作(图解) 信号与槽的创建 信号的声明 槽函数的声明 槽函数的实现 (无重载)信号 ...

  6. pyqt5中信号与槽的认识

    一.介绍 信号(Signal)和槽(Slot)是Qt中的核心机制,也是PyQt变成中对象之间进行通信的机制 在pyqt5中,每一个QObject对象和pyqt中所有继承自QWidget的控件都支持信号 ...

  7. Python Qt GUI设计:多线程中信号与槽的使用(基础篇—9)

    有时候在项目开发时,经常会遇到一些耗时操作导致界面迟缓卡顿,为了解决这个问题,可以创建多线程,使用主线程更新界面使用子线程实时处理数据,最后将结果显示到界面上. PyQt多线程将使用QThread函数 ...

  8. QT中信号和槽的简单解释

    代码见: https://blog.csdn.net/cloud323/article/details/82192241 思想介绍为: 一.信号 当对象的内部状态发生改变,信号就被发射.只有定义了一个 ...

  9. QT中信号与槽的常见使用

    一.标准信号与槽函数 有些信号和槽的函数模板都已经被写好了,只需要调用即可 下面是一个点击按钮关闭窗口的程序 .h文件 #ifndef MYWIDGET_H #define MYWIDGET_H#in ...

最新文章

  1. Thrift在windows下的使用
  2. 读秦小波《设计模式之禅》 -- 工厂模式
  3. central maven_一键发布到Maven Central的方法
  4. MultipartFile和CommonsMultipartFile的区别!
  5. centos mysql 实战 第一节课 安全加固 mysql安装
  6. js类型判断(数字、0、、undefined、null)
  7. spring与web关系
  8. AcWing 827. 双链表
  9. vue 动画 抖动效果_css开发常用动画
  10. linux高级编程进程,linux高级编程(五、进程)
  11. windows便签快捷键_Windows10便签快捷键在哪里设置?
  12. java 8中Comparator.comparing和reversed
  13. 计算机应用基础论文1000字,计算机应用基础论文.doc
  14. appium怎么测试ios_使用Appium,Cucumber和Serenity测试iOS应用程序—质量秘诀
  15. DHCP Relay的介绍
  16. 疫情之后,教育的生死淘汰赛
  17. 小米typec转miniDP扩展坞评测
  18. VMware Workstation Pro界面设置为中文界面
  19. 除了四大“门派”菌,一文了解肠道菌群的其它17个小众“门派”细菌
  20. 起点导航系统源码最新V2.6开源可运营版

热门文章

  1. 【JAVASCRIPT】无刷新评论
  2. oenwrt 进不了bios_win7进不了bios原因及解决办法
  3. java开发怎么打补丁_[Java教程]【NC】出补丁与打补丁
  4. Java黑皮书课后题第3章:**3.22(几何:点是否在圆内)编写程序,提示用户输入一个点(x,y),然后检查这个点是否在以(0,0)为圆心、半径为10的圆内
  5. Linux SWAP 交换分区
  6. Shell基础知识(三)
  7. Java Class Loader Retrospect
  8. sftp 服务器外网访问设置
  9. 关于extjs中动态添加TabPanel的tab项并以iframe显示的整理(转)
  10. PHP-代码审计-XSS