信号和槽

2023-11-18

信号和槽用于对象间的通讯。信号/槽机制是Qt的一个中心特征并且也许是Qt与 其它工具包的最不相同的部分。

在图形用户界面编程中,我们经常希望一个窗口部件的一个变化被通知给另一个 窗口部件。更一般地,我们希望任何一类的对象可以和其它对象进行通讯。例如,如 果我们正在解析一个XML文件,当我们遇到一个新的标签时,我们也许希望通知列表 视图我们正在用来表达XML文件的结构。

较老的工具包使用一种被称作回调的通讯方式来实现同一目的。回调是指一个函 数的指针,所以如果你希望一个处理函数通知你一些事件,你可以把另一个函数(回 调)的指针传递给处理函数。处理函数在适当的时候调用回调。回调有两个主要缺 点。首先他们不是类型安全的。我们从来都不能确定处理函数使用了正确的参数来调 用回调。其次回调和处理函数是非常强有力地联系在一起的,因为处理函数必须知道 要调用哪个回调。

一个关于一些信号和槽连接的摘要图

在Qt中我们有一种可以替代回调的技术。我们使用信号和槽。当 一个特定事件发生的时候,一个信号被发射。Qt的窗口部件有很多预定义的信号, 但是我们总是可以通过继承来加入我们自己的信号。槽就是一个可以被调用处理特定 信号的函数。Qt的窗口部件又很多预定义的槽,但是通常的习惯是你可以加入自己的 槽,这样你就可以处理你所感兴趣的信号。

信号和槽的机制是类型安全的:一个信号的签名必须与它的接收槽的签名相匹 配。(实际上一个槽的签名可以比它接收的信号的签名少,因为它可以忽略额外的 签名。)因为签名是一致的,编译器就可以帮助我们检测类型不匹配。信号和槽是 宽松地联系在一起的:一个发射信号的类不用知道也不用注意哪个槽要接收这个信 号。Qt的信号和槽的机制可以保证如果你把一个信号和一个槽连接起来,槽会在正 确的时间使用信号的参数而被调用。信号和槽可以使用任何数量、任何类型的参 数。它们是完全类型安全的:不会再有回调核心转储(core dump)。

QObject类或者它的一个子类 (比如QWidget类)继承的所有类可以包含信号和槽。 当对象改变它们的状态的时候,信号被发送,从某种意义上讲,它们也许对外面的 世界感兴趣。这就是所有的对象通讯时所做的一切。它不知道也不注意无论有没有 东西接收它所发射的信号。这就是真正的信息封装,并且确保对象可以用作一个软 件组件。

一个信号和槽连接的例子

槽可以用来接收信号,但它们是正常的成员函数。一个槽不知道 它是否被任意信号连接。此外,对象不知道关于这种通讯机制和能够被用作一个真正 的软件组件。

你可以把许多信号和你所希望的单一槽相连,并且一个信号也可以和你所期望的 许多槽相连。把一个信号和另一个信号直接相连也是可以的。(这时,只要第一个信 号被发射时,第二个信号立刻就被发射。)

总体来看,信号和槽构成了一个强有力的组件编程机制。

一个小例子

一个最小的C++类声明如下:

    class Foo
    {
    public:
        Foo();
        int value() const { return val; }
        void setValue( int );
    private:
        int val;
    };

一个小的Qt类如下:

    class Foo : public QObject
    {
        Q_OBJECT
    public:
        Foo();
        int value() const { return val; }
    public slots:
        void setValue( int );
    signals:
        void valueChanged( int );
    private:
        int val;
    };

这个类有同样的内部状态,和公有方法来访问状态,但是另外它也支持使用信号 和槽的组件编程:这个类可以通过发射一个信号,valueChanged(),来告 诉外面的世界它的状态发生了变化,并且它有一个槽,其它对象可以发送信号给这个 槽。

所有包含信号和/或者槽的类必须在它们的声明中提到Q_OBJECT。

槽可以由应用程序的编写者来实现。这里是Foo::setValue()的一个可能的实现:

    void Foo::setValue( int v )
    {
        if ( v != val ) {
            val = v;
            emit valueChanged(v);
        }
    }

emit valueChanged(v)这一行从对象中发射valueChanged信 号。正如你所能看到的,你通过使用emit signal(arguments)来发射信号。

下面是把两个对象连接在一起的一种方法:

    Foo a, b;
    connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
    b.setValue( 11 ); // a == undefined  b == 11
    a.setValue( 79 ); // a == 79         b == 79
    b.value();        

调用a.setValue(79)会使a发射一个valueChanged() 信号,b将会在它的setValue()槽中接收这个信号,也就是b.setValue(79) 被调用。接下来b会发射同样的valueChanged()信号,但是因为 没有槽被连接到bvalueChanged()信号,所以没有发生任何事 (信号消失了)。

注意只有当v != val的时候setValue()函数才会设置这个值 并且发射信号。这样就避免了在循环连接的情况下(比如b.valueChanged() 和a.setValue()连接在一起)出现无休止的循环的情况。

这个例子说明了对象之间可以在互相不知道的情况下一起工作,只要在最初的时在 它们中间建立连接。

预处理程序改变或者移除了signalsslotsemit 这些关键字,这样就可以使用标准的C++编译器。

在一个定义有信号和槽的类上运行moc。这样就会生成 一个可以和其它对象文件编译和连接成引用程序的C++源文件。

信号

当对象的内部状态发生改变,信号就被发射,在某些方面对于 对象代理或者所有者也许是很有趣的。只有定义了一个信号的类和它的子类才能发射 这个信号。

例如,一个列表框同时发射highlighted()activated()这 两个信号。绝大多数对象也许只对activated()这个信号感兴趣,但是有时 想知道列表框中的哪个条目在当前是高亮的。如果两个不同的类对同一个信号感兴趣, 你可以把这个信号和这两个对象连接起来。

当一个信号被发射,它所连接的槽会被立即执行,就像一个普通函数调用一样。 信号/槽机制完全不依赖于任何一种图形用户界面的事件回路。当所有的槽都返回后 emit也将返回。

如果几个槽被连接到一个信号,当信号被发射时,这些槽就会被按任意顺序一个 接一个地执行。

信号会由moc自动生成并且一定不要在.cpp文件中 实现。它们也不能有任何返回类型(比如使用void)。

关于参数需要注意。我们的经验显示如果信号和槽使用特殊的类型, 它们都可以多次使用。如果QScrollBar::valueChanged() 使用了一个特殊的类型,比如hypothetical QRangeControl::Range,它就只能被连 接到被设计成可以处理QRangeControl的槽。简 单的和教程1的第5部分一样的程序将是不可 能的。

当一个和槽连接的信号被发射的时候,这个操被调用。槽也是 普通的C++函数并且可以像它们一样被调用;它们唯一的特点就是它们可以被信号连 接。槽的参数不能含有默认值,并且和信号一样,为了槽的参数而使用自己特定的类 型是很不明智的。

因为槽就是普通成员函数,但却有一点非常有意思的东西,它们也和普通成员函 数一样有访问权限。一个槽的访问权限决定了谁可以和它相连:

一个public slots:区包含了任何信号都可以相连的槽。这对于组件编 程来说非常有用:你生成了许多对象,它们互相并不知道,把它们的信号和槽连接起 来,这样信息就可以正确地传递,并且就像一个铁路模型,把它打开然后让它跑起来。

一个protected slots:区包含了之后这个类和它的子类的信号才能连接 的槽。这就是说这些槽只是类的实现的一部分,而不是它和外界的接口。

一个private slots:区包含了之后这个类本身的信号可以连接的槽。这 就是说它和这个类是非常紧密的,甚至它的子类都没有获得连接权利这样的信任。

你也可以把槽定义为虚的,这在实践中被发现也是非常有用的。

信号和槽的机制是非常有效的,但是它不像“真正的”回调那样快。信号和槽稍 微有些慢,这是因为它们所提供的灵活性,尽管在实际应用中这些不同可以被忽略。 通常,发射一个和槽相连的信号,大约只比直接调用那些非虚函数调用的接收器慢 十倍。这是定位连接对象所需的开销,可以安全地重复所有地连接(例如在发射期 间检查并发接收器是否被破坏)并且可以按一般的方式安排任何参数。当十个非虚 函数调用听起来很多时,举个例子来说,时间开销只不过比任何一个“new”或者 “delete”操作要少些。当你执行一个字符串、矢量或者列表操作时,需要“new”或者 “delete”,信号和槽仅仅对一个完整函数调用地时间开销中的一个非常小的部分负 责。无论何时你在一个槽中使用一个系统调用和间接地调用超过十个函数的时间是 相同的。在一台i585-500机器上,你每秒钟可以发射2,000,000个左右连接到一个 接收器上的信号,或者发射1,200,000个左右连接到两个接收器的信号。信号和槽 机制的简单性和灵活性对于时间的开销来说是非常值得的,你的用户甚至察觉不出来。

元对象信息

元对象编译器(moc) 解析一个C++文件中的类声明并且生成初始化元对象的C++代码。元对象包括所有信号 和槽函数的名称,还有这些函数的指针。(要获得更多的信息,请看为什么Qt不用模板来实现信号和槽?

元对象包括一些额外的信息,比如对象的类名称。 你也可以检查一个对象是否继承了一个特定的类, 比如:

  if ( widget->inherits("QButton") ) {
        // 是的,它是一个Push Button、Radio Button或者其它按钮。
  }

一个真实的例子

这是一个注释过的简单的例子(代码片断选自qlcdnumber.h)。

    #include "qframe.h"
    #include "qbitarray.h"

    class QLCDNumber : public QFrame

QLCDNumber通过QFrameQWidget,还有#include这样的相关声明继承了含有绝大 多数信号/槽知识的QObject

    {
        Q_OBJECT

Q_OBJECT是由预处理器展开声明几个由moc来实现的成员函数,如果你得到了几行 “virtual function QButton::className not defined”这样的编译器错误信息,你也 许忘记运行moc或者忘记在连接命令中包含moc输出。

    public:
        QLCDNumber( QWidget *parent=0, const char *name=0 );
        QLCDNumber( uint numDigits, QWidget *parent=0, const char *name=0 );

它并不和moc直接相关,但是如果你继承了QWidget,你当然想在你的构造器中获 得parentname这两个参数,而且把它们传递到父类的构造器中。

一些解析器和成员函数在这里省略掉了,moc忽略了这些成员函数。

    signals:
        void    overflow();

QLCDNumber被请求显示一个不可能值时,它 发射一个信号。

如果你没有留意溢出,或者你认为溢出不会发生,你可以忽略overflow()信号, 也就是说你可以不把它连接到任何一个槽上。

另一方面如果当数字溢出时,你想调用两个不同的错误函数,很简单地你可 以把这个信号和两个不同的槽连接起来。Qt将会两个都调用(按任意顺序)。

    public slots:
        void    display( int num );
        void    display( double num );
        void    display( const char *str );
        void    setHexMode();
        void    setDecMode();
        void    setOctMode();
        void    setBinMode();
        void    smallDecimalPoint( bool );

一个槽就是一个接收函数,用来获得其它窗口部件状态变或的信息。QLCDNumber 使用它,就像上面的代码一样,来设置显示的数字。因为display()是这个 类和程序的其它的部分的一个接口,所以这个槽是公有的。

几个例程把QScrollBar的newValue信号连接到display槽,所以LCD数字可以继续显示滚动条的值。

请注意display()被重载了,当你把一个信号和这个槽相连的时候,Qt将会选择适 当的版本。如果使用回调,你会发现五个不同的名字并且自己来跟踪类型。

一些不相关的成员函数已经从例子中省略了。

    };

FROM:  http://www.kuqin.com/qtdocument/signalsandslots.html




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

信号和槽 的相关文章

  • 如何正确理解开漏输出和推挽输出

    作者 知乎用户 链接 https www zhihu com question 28512432 answer 41217074 来源 知乎 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 我觉得下面这个 网上资料 还是
  • java项目如何实现数据恢复_Java web 项目中对数据库备份和恢复

    说白了 还是去调用cmd实现数据库的备份和还原功能 备份 mysqldump hserverUrl uusername ppassword dbname gt savePath 还原 mysql hserverUrl uusername p
  • 一文读懂SpringCloud全家桶

    一 云原生应用 SpringCloud是对Springboot使用的分布式解决方案 适合分布式 中大型的项目架构开发 现在也逐渐成为Java服务端的主流框架 使用Spring Cloud开发的应用程序非常适合在Docker和PaaS 比如P
  • C# 提交报错:Validation of viewstate MAC failed 解决办法

    出现以下报错 验证视图状态 MAC 失败 如果此应用程序由网络场或群集承载 请确保
  • 【STM32】SPI初步使用 读写FLASH W25Q64

    硬件连接 1 SS Slave Select 从设备选择信号线 常称为片选信号线 每个从设备都有独立的这一条 NSS 信号线 当主机要选择从设备时 把该从设备的 NSS 信号线设置为低电平 该从设备即被选中 即片选有效 接着主机开始与被选中
  • MySQL 8.0 多实例的配置应用

    文章目录 同版本多实例 配置 部署 启动 连接 不同版本多实例 配置 初始化 initialize insecure 含义 启动 同版本多实例 配置 mkdir p data 330 7 9 data chown R mysql mysql
  • 同城双活与异地多活架构分析

    本文首发于 vivo互联网技术 微信公众号 链接 https mp weixin qq com s OjfFcjnGWV5kutxXndtpMg 作者 vivo官网商城开发团队 采用高可用系统架构支持重要系统 为关键业务提供7x24的不间断
  • Win32 UDP Socket通信学习

    学习内容 参见 Windows网络编程 第7章 Winsock基础 与TCP流式协议不同 UDP为数据报协议 服务端接受数据 客户端发送数据 UDP服务端流程 Socket或WSASocket建立套接字 用SOCK DGRAM标志 bind
  • bootstrap--栅格系统详解(源码分析)

    目录 1 bootstrap是什么 2 栅格模型设计的精妙之处 3 预备知识 4 栅格系统的框架 4 1bootstrap容器 4 1 1固定容器与流体容器公共样式 4 1 2固定容器样式 4 2 bootstrap行与列 4 2 1行 4
  • js中的navigator对象 用js判断浏览器类型

    navigator对象 window navigator返回一个navigator对象的引用 可以用它来查询一些关于运行当前脚本的应用程序的相关信息 在浏览器打印一下navigator对象 console log navigator MDN
  • 基于Nonconvex规划的配电网重构研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 本文基于Nonconvex规划的配电网重构
  • FastAPI从入门到实战(8)——一文弄懂Cookie、Session、Token与JWT

    看到标题应该也能看出来本文讲的就是前端鉴权相关的内容了 鉴权也就是身份认证 指验证用户是否有系统的访问权限 只要是web开发 这部分内容就是不可能不学的 很多面试也必问 所以本文就针对此主题详细记录一下其常见的几种方式 HTTP状态 HTT
  • 面试知识点梳理及相关面试题(五)-- 多线程

    1 进程 线程和协程 进程 一个运行的程序就是一个进程 进程是资源分配的最小单位 线程 程序中运行的一个个子任务就是一个线程 线程是操作系统调度执行的最小单位 协程 协程是一种用户态的轻量级线程 协程的调度完全由用户控制 2 创建线程的四种
  • ue4_timeline时间轴

    1 给一个cube添加蓝图 需要修改的是z轴方向移动位置 将z轴传入时间轴 时间轴蓝图如下 z轴时间轴修改为 第一个节点 time 0 value 300 物体的z轴初始位置 第二个节点 time 1 value 600 z轴移动300个单
  • 【python3】3.函数、类、模块

    2022 11 15 本学习内容总结于莫烦python 3 函数 类 模块 https mofanpy com tutorials python basic interactive python function 1 Function 函数
  • IB计算机科学选课,IB体系应如何正确选课?

    原标题 IB体系应如何正确选课 选择国际学校最重要的环节就是选课 这决定了你之后的学习方向和计划 甚至影响到成绩的好坏 目前很多国际学校都是IB体系 IB课程除了重视基础知识教学 还有学生的综合素质培养 教学大纲也是与时俱进5年一换 IB体
  • Vue3 + Vite3 + Element-Plus 管理系统

    Vue3 Vite3 Element Plus 管理系统 介绍 vue diverse admin 基于 Vue3 TypeScript Vite3 Element Plus 开源的一套后台管理模板 项目均使用JS开发 但已经配置TS 可以

随机推荐

  • 【VHDL】随机存储器RAM、只读存储器ROM

    VHDL 随机存储器设置 RAM程序 LIBRARY IEEE USE IEEE STD LOGIC 1164 ALL ENTITY RAM 8 IS PORT CS RD WR IN STD LOGIC CLK IN STD LOGIC
  • Android SDK的安装教程

    Android SDK的安装教程 Android4 1虽说已经发布了好些天 但由于的我手机比较坑 系统依旧保持在2 3 4 0的都是可望不可即的了 就别说4 1 由于资金的问题 没法换手机 只能另想方法 通过在笔记本上装android4 1
  • java实现,如何在当前时间往后推三十天

    Calendar cal Calendar getInstance cal add Calendar Date 30 Date date cal getTime
  • topaz全家桶中文_apache 开源项目全家桶 2020年12月中文介绍

    apache项目全家桶 很多apache开源项目特别好 但是没什么能汇总的文档 我来整一个 不定期更新 big data 50 Project Apache Accumulo star 838 github https github com
  • shell脚本基础4——function函数、expect

    文章目录 一 function函数 1 1 函数的定义使用 1 2 函数参数 1 2 1 脚本内传参 1 2 2 脚本外传参 1 3 引用局部变量 1 3 1 区分局部变量 1 3 2 全局变量在函数外 1 3 3 全局变量在函数体 1 3
  • windows检测文件夹是否更新.bat脚本 windows循环检查文件夹

    最近在写windows脚本 记录一下一个很有用的信息 参考 https zhidao baidu com question 2208474390884363868 html 转载于 https www cnblogs com ChenCha
  • Python3 使用 matplotlib 画折线图

    ChartUtil py import matplotlib pyplot as plt from pylab import mpl def plotLine xData yData xLabel chartTitle mpl rcPara
  • matlab:基本操作与矩阵输入

    学习素材 MATLAB教程 台大郭彦甫 14课 原视频补档 MATLAB教程 台大郭彦甫 14课 原视频补档 哔哩哔哩 bilibili 部分素材使用视频截图 目录 一 基本运算 二 关键字 三 format 四 符号 1 2 colon
  • [ACTF 2020 新生赛] Exec

    Exec ping 127 0 0 1 ls ping 127 0 0 1 nl f
  • ABAP实现粘贴板的操作,复制粘贴_SAP刘梦_新浪博客

    ABAP可以实现复制粘贴的操作 自己玩儿 项目里暂时用不到 在ABAP中的 CLASS CL GUI FRONTEND SERVICES中提供了两个方法来控制与剪贴板内容的导入导出 分别是 CLIPBOARD EXPORT和CLIPBOAR
  • Git学习笔记【1】---本地操作

    声明 本教程参考自https www bilibili com video BV1Zz4y1C7vg p 17 一 Git的安装 官网下载即可 https git scm com 二 初始化本地仓库 1 创建一个文件夹 2 打开Git终端
  • Java常用System类

    java lang System类中提供了大量的静态方法 可以获取与系统相关的信息或系统级操作 在System类的API文档中 常用的方法有 public static long currentTimeMillis 返回以毫秒为单位的当前时
  • 移动互联网终端的touch事件,touchstart, touchend, touchmove

    前言 如果我们允许用户在页面上用类似桌面浏览器鼠标手势的方式来控制WEB APP 这个页面上肯定是有很多可点击区域的 如果用户触摸到了那些可点击区域怎么办呢 诸如智能手机和平板电脑一类的移动设备通常会有一个电容式触摸屏 capacitive
  • 微信小游戏关系链能不能获取到服务器,关系链互动数据

    关系链互动数据 在开放 关系链数据能力 的基础上 小游戏新增 互动型托管数据 提供关系链互动能力 用于实现小游戏内微信好友互动 点赞 送礼物等 的功能 关系链互动数据能力支持 好友间互赠 50 种游戏内道具 提供成功互动后的主域回调 满足互
  • maven项目引入外部文件(资源)

    1 maven项目如何引入外部文件 maven项目一般在main目录下分为java和resources 前者是源码 后者是资源 含外部文件 在maven的pom xml中指定resources所在目录后 当maven编译时 resource
  • python3+requests:接口自动化测试(二)

    前言 上篇文章python3 requests unittest 接口自动化测试 一 已经介绍了基于unittest框架的实现接口自动化 但是也存在一些问题 比如最明显的测试数据和业务没有区分开 接口用例不便于管理等 所以又对此修改完善 接
  • OPENCV角点检测和亚像素级检测

    首先进行粗检测 函数goodFeaturesToTrack 存储进入corners中 然后cornerSubPix函数进行亚像素精确匹配 设置结束条件 由于实际应用中线条较粗 因此 CORNER BLOCKSIZE 9 CORNER QUA
  • STM32 ---deley延时两行代码实现【为方便移植文件】

    一 前提简述 优点 无需定时器 仅两行代码 缺点 不够精准 应用理由 很多文件移植后 发现delay都需要外部文件支持 那 不如在文件内定义一个 方便各文件移植 应用要求 STM32F103系列 默认72MHz系统时钟 任何一个工程 复制粘
  • ERP仓库管理系统需求

    ERP仓库管理系统需求 目录 1 系统管理 2 2 供货管理 12 3 仓库管理 24 4 出货管理 27 一 系统管理 1 输入账号 密码进入系统 功能 可能出现以下情况 1 1账号或密码错误 或者员工已经辞职离开 1 2账号不存在 1
  • 信号和槽

    信号和槽用于对象间的通讯 信号 槽机制是Qt的一个中心特征并且也许是Qt与 其它工具包的最不相同的部分 在图形用户界面编程中 我们经常希望一个窗口部件的一个变化被通知给另一个 窗口部件 更一般地 我们希望任何一类的对象可以和其它对象进行通讯