QT信号槽机制

2023-11-11

信号槽

信号槽是QT中用于对象间通信的一种机制,也是QT的核心机制。在GUI编程中,我们经常需要在改变一个组件的同时,通知另一个组件做出响应。例如:
610439-20151125210547171-1455928966.png

一开始我们的Find按钮是未激活的,用户输入要查找的内容后,查找按钮就被激活,这就是输入框与Find按钮这两个组件间通信的例子。
610439-20151125210557702-1156584930.png

早期,对象间的通信采用回调来实现。回调实际上是利用函数指针来实现,当我们希望某件事发生时处理函数能够获得通知,就需要将回调函数的指针传递给处理函数,这样处理函数就会在合适的时候调用回调函数。回调有两个明显的缺点:

  • 它们不是类型安全的,我们无法保证处理函数传递给回调函数的参数都是正确的。
  • 回调函数和处理函数紧密耦合,源于处理函数必须知道哪一个函数被回调。

信号与槽

在QT中,我们有回调技术之外的选择,也即是信号槽机制。所谓的信号与槽,其实都是函数。当特定事件被触发时(如在输入框输入了字符)将发送一个信号,而与该信号建立的连接槽,则可以接收到该信号并做出反应(激活Find按钮)。
QT组件预定义了很多信号和槽,而在GUI编程中,我们习惯于继承那些组件,继承后添加我们自己的槽,以便以我们的方式来处理信号。槽和普通的C++成员函数几乎是一样的,它可以是虚函数,可以被重载,可以是共有、私有或是保护的,也同样可以被其他成员函数调用。它的函数参数也可以是任意类型的。唯一不同的是:槽还可以和信号连接在一起。
与回调不同,信号槽机制是类型安全的。这体现在信号的函数签名与槽的函数签名必须匹配上,才能够发生信号的传递。实际上,槽的参数个数可以比信号的参数个数少,因为槽能够忽略信号形参中多出来的参数。信号和槽是松耦合的:发出信号的类不关心哪些类将接收它的信号。QT的信号槽机制吧哦这里在正确的时间,槽能够接收到信号的参数并调用。信号和槽都可以有任意个数的参数,它们都是类型安全的。

自定义信号和槽的一个例子

首先我们要知道的是,所有继承自QObject或者它的子类(如QWidget)都可以包含信号槽。我们写的类须继承自QObject(或其子类)。所有包含了信号槽的类都必须在声明的上部含有Q_OBJECT宏。
一个基于QObject的C++简单类:

//MyStr.h
# ifndef  MYSTR
# define  MYSTR
#include<QObject>
#include<QString>
 
class MyStr :public QObject
{
    Q_OBJECT //必须包含的宏
 
public:
    MyStr (){m_value = "zero";}
 
    QString value() const {return  m_value;}
 
public slots :
    void setValue(QString value );
 
    signals: //信号
    void valueChanged(QString newValue); 
private:
    QString m_value;
};
 
#endif 
  • 在这个简单的类中,我们可以看到,使用slots来表示槽,而使用signals来表示信号。实际上没有那么神秘,它们都是宏定义,甚至signals只是public的宏定义:
#     define signals public
  • Signal的代码会由 moc 自动生成,开发人员一定不能在自己的C++代码中实现它。

  • 反之,槽应该由编程人员来实现,下面提供MyStr::setVaule()的一种可能实现

#include"MyStr.h"
void MyStr::setValue(QString value)
{
    if(value != m_value)
    {
        m_value = value;
        emit valueChanged(value);
    }
}

setValue函数首先比较新参的值与数据成员的值是否是一样的(后面有解释为何这样做),如果不是,则设置好数据成员m_value的值,然后,把信号valueChanged()发送出去。发送给谁?类并没有写,这并不是类设计者所关心的,也不是类所关心的,它只管把信号发送出去就行。然后,我们再来设置谁来接收这个信号。

int main(int argc, char *argv[])
{
    MyStr a;
    MyStr b;
    QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString)));
    a.setValue("this is A");
    return 0;
}

我们定义了两个类对象a/b,使用 QObject::connect()函数指定了发送方、信号、接收方、槽等信息,connect函数的格式如下:

 QObject::connect(    发送方,    SIGNAL(...),    接收方,    SLOT(..)    );

当我们调用a的成员函数setValue时,该函数除了把a.m_value设置为"this is A",也把信号valueChanged()发送出去,被b.setValue所接收,从而,把b.m_value设置为"this is A",同时b.setValue又把valueChanged信号发射出去,然而该信号并没有对象接收,因为我们没有建立以b为发送方的任何连接。此时你应该明白,为何在emit前需要判断value != m_value,因为如果没有此步骤,且恰巧设置了

QObject::connect(&b,SIGNAL(valueChanged(QString)),&a,SLOT(setValue(QString)));

则b的信号被a接收,a又发送信号被b接收,如此进入死循环。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
 
    MyStr  a;
    MyStr  b;
    QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString)));
    a.setValue("this is A");
 
    QLabel* label = new QLabel;
    label->setText( b.value());
    label->show();
    return app.exec();
}

我们使用label输出来看看b是否接收到a的信号,如果是,则b的内容应该是"this is A",输出在label上,程序运行结果:

610439-20151125210611671-782906497.png

这个例子展示了对象之间通信的一种方式。对象间可以一起工作,而不需要知道彼此的任何信息。为了达到通信的目的,只需要将它们连接起来,而这只需要通过 调用 QObject::connect() 函数指定一些简单信息就好。

细节

连接

要把信号成功连接到槽,它们的参数必须具有相同的顺序和相同的类型,或者允许信号的参数比槽多,槽会自动忽略掉多出来的参数而进行调用。

一个信号可以连接多个槽

使用QObject::connect可以把一个信号连接到多个槽,而当信号发射时,将按声明联系时的顺序依次调用槽。

 MyStr  a;
 MyStr  b;
 MyStr  c;
//信号连接到两个槽
 QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString)));
 QObject::connect(&a,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
 a.setValue("this is A");
//依次调用b.setValue()、c.setValue()

多个信号可以连接同一个槽

同样的,可以让多个信号连接到同一个槽上 ,而且其中的每一个信号的发送,都会调用了那个槽。

 MyStr  a;
 MyStr  b;
 MyStr  c;
//两个信号连接到同一个槽
 QObject::connect(&a,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
 QObject::connect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
//下面的操作皆会调用到槽c.setValue()
 a.setValue("this is A");
b.setValue("this is B");

一个信号可以和另外一个信号相连接

当发射第一个信号的时候,也会把第二个信号一个发送出去。

 MyStr  a;
 MyStr  b;
 MyStr  c;
//两个信号相连接
 QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SIGNAL(valueChanged(QString)));
//再建立b与c的连接
 QObject::connect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
//下面的操作同时发送了信号a.valueChanged与b.valueChanged
 a.setValue("this is A");
//从而信号b.valueChanged被槽c.setValue所接收

连接可以被移除

//移除b 与 c之间的连接
  QObject::disconnect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));

实际上当对象被delete时,其关联的所有链接都会失效,QT会自动移除和这个对象的所有链接。

感谢您耐心的阅读。

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

QT信号槽机制 的相关文章

随机推荐

  • 开发中 @/方式引入的文件,通过ctrl + 点击可直接指向

    问题描述 实际开发项目中 例如VUE 或者 React 开发中 我们经常会通过 的方式进行文件引入 但是这种方式会导致不能直接点击进入指定的文件 解决方案 根目录创建jsconfig json文件 文件内容如下 exclude node m
  • Vue实现搜索关键词高亮显示

    最近写移动端项目的时候 遇到搜索关键词高亮的需求 写篇文章纪录一下 先看效果 以上为实现效果展示 整体思路 对后台返回的数据进行操作 我这里是模拟数据 使用正则去匹配搜索关键词后 使用replace进行字符串的替换 渲染数据部分使用v ht
  • CentOS-6.6-x86_64-minimal.iso镜像下载

    CentOS6 6的64位镜像文件iso 迅雷种子 centos是基于linux的内核开发的操作系统 是企业服务器广泛使用的操作系统 链接 https pan baidu com s 1tSpIdAxTnScqXQotVCg2gg http
  • ARM Cortex-A7时钟树综合实战分析

    ARM Cortex A7时钟树综合实战分析 文章右侧广告为官方硬广告 与吾爱IC社区无关 用户勿点 点击进去后出现任何损失与社区无关 吾爱 IC 高端技术交流社区 知识星球目前已经拥有1040 位星球成员 目前数字 IC 领域最大的高端技
  • 判断玩家是否在敌人的可视范围的方法Physics.OverlapSphere

    Physics OverlapSphere Vector 3 position float radius 该方法判断以一个点为中心 半径为radius的球中的碰撞体集合 所以返回值可以用 var 如var colliders Physics
  • spring mvc 控制器返回 HTML 页面

    之前的页面都是 jsp的 后面需要改为html 如下代码 之前的Log 是 jsp 后面改为 html Log html 页面单独运行正常 但是在控制器跳转是出错 如下 RequestMapping value login out publ
  • HCIPR&S222-V2.5一些总结

    1 IP Precedence取值中 代表immediate的是2 2 VRRP中路由器已经为Master设备 不会被更高优先级的Backup设备抢占 如果出现故障的情况会被Backup设备抢占 故障恢复后重新抢占为Master设备 默认为
  • java jmf视频播放器无法播放视频问题

    初学java 谢了一个参照网上代码写了个java jmf视频播放器 运行调试代码没问题 可是被这个avi视频格式纠结的要死 换了n多个avi格式视频依然不行 经过查阅资料发现现在网上流行的avi格式并不是真正的avi格式 大都是其他格式转化
  • 原生AJAX 的基本使用

    1 准备工作 1 1 安装node js Node js 中文网 1 2 安装express 服务端框架 Express 基于 Node js 平台的 web 应用开发框架 Express 中文文档 Express 中文网 1 2 1 初始
  • MYSQL多表连查

    前言 多表查询 也称为关联查询 指两个或更多个表一起完成查询操作 前提条件 这些一起查询的表之间是有关系的 一对一 一对多 它们之间一定是有关联字段 这个关联字段可能建立了外键 也可能没有建立外键 比如 员工表和部门表 这两个表依靠 部门编
  • 诚邀您参加Go+1.0发布会!与大咖共同探索行业生态!

    你想参与iPhone13抽奖吗 你想和大咖面对面交流吗 你想收获最前沿的行业动态吗 10 月15 日 Go Together Go 1 0发布会暨Go 开发者基金会启动仪式重磅来袭 活动中除七牛云 CEO Go 语言发明人许式伟以及Go 开
  • 传奇私服游戏支付接口申请(已解决)

    传奇游戏是一款经典打怪升级 PK游戏 盛大游戏公司于2001年9月发布 随后出现了众多传奇私服游戏版本 到现在依然受很多人喜爱 经历了多次升级换代 现已转变为游戏体验更加细腻的页游和手游 成为了小成本高收益的热门游戏 受到各游戏服务商和玩家
  • IO流基础总结

    IO流 File 1 File类概述和构造方法 File 它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言 其封装的并不是一个真正存在的文件 仅仅是一个路径名 而已 它可以是存在的 也可以是不存在的
  • Allegro PCB设计中:结构文件DXF导入、更新、PCB板框更改

    1 将结构工程师输出的DXF文件导入到Allegro PCB设计中 文章来源地址https www yii666 com blog 453846 html action onAll 2 结构文件DXF多次更改导致PCB板框尺寸涉及的修改 1
  • C++STL:vector

    C STL中的vector是一种动态分配的数组 可以在运行时动态增加和删除元素 它也是C 中最常用的容器之一 以下是vector的使用介绍 引入头文件 在使用vector之前 需要添加头文件 include
  • nrm 源管理

    什么是nrm nrm 是一个 npm 源管理器 你可以快速地在 npm源间切换 大家在开发中可能会经常切换 npm 源 我们会使用命令切换 如 npm set registry https registry npm taobao org 或
  • Vue style中的 scoped 属性

    Vue 中存在 scoped 属性 HTML5中也存在一个 scoped 属性 而且 这两者都是针对 css 样式处理的属性 所以很多文章在 解释 Vue scoped 的时候 都会把两者混为一谈 直接进把 HTML5 scoped 的定义
  • Login控件用法,用自己的数据库提供login控件的数据源

    一 在配置web config文件 web config 文件
  • 基于SuperSocket 1.6版本的自定义帧过滤的源码分析(实现MODBUS通信)

    一 SuperSocket 1 6 自定义帧过滤的官方文档地址 http docs supersocket net v1 6 zh CN Implement Your Own Communication Protocol with IReq
  • QT信号槽机制

    信号槽 信号槽是QT中用于对象间通信的一种机制 也是QT的核心机制 在GUI编程中 我们经常需要在改变一个组件的同时 通知另一个组件做出响应 例如 一开始我们的Find按钮是未激活的 用户输入要查找的内容后 查找按钮就被激活 这就是输入框与