【Qt】信号和槽

目录

前言

今天接着【Qt】Qt初识详情点击查看来介绍【Qt】信号和槽

一、信号和槽的基本概念

  • 在Qt中,用户和控件的每次交互过程称为⼀个事件。比如"用户点击按钮"是⼀个事件,"用户关闭窗口"也是⼀个事件。每个事件都会发出⼀个信号,例如用户点击按钮会发出"按钮被点击"的信号,用户关闭窗口会发出"窗口被关闭"的信号
  • Qt中的所有控件都具有接收信号的能力,⼀个控件还可以接收多个不同的信号 。对于接收到的每个信号,控件都会做出相应的响应动作(按钮所在的窗口接收到"按钮被点击"的信号后,会做出"关闭自己"的响应动作;再比如输⼊框自己接收到"输⼊框被点击"的信号后,会做出"显示闪烁的光标,等待用户输入数据"的响应动作)。在Qt中,对信号做出的响应动作就称之为槽。
  • 信号和槽是Qt特有的消息传输机制,它能将相互独立的控件关联起来。比如,"按钮"和"窗⼝"本身是两个独立的控件,点击"按钮"并不会对"窗口"造成任何影响。通过信号和槽机制,可以将"按钮"和"窗⼝"关联起来,实现"点击按钮会使窗口关闭"的效果。

1、信号

信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时Qt对应的窗口类会发出某个信号,以此对用户的操作做出反应。因此,信号的本质就是事件,如:

  • 按钮单击、双击
  • 窗口刷新
  • 鼠标移动、鼠标按下、鼠标释放
  • 键盘输入

在Qt中信号是通过什么形式呈现给使用者的呢?

  • 我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些被触发的事件
  • 对于使用者来说触发了⼀个事件我们就可以得到Qt框架给我们发出的某个特定信号
  • 信号的呈现形式就是函数,也就是说某个事件产生了,Qt框架就会调用某个对应的信号函数,通知使用者。

在Qt中信号的发出者是某个实例化的类对象

2、槽

  1. 槽(Slot)就是对信号响应的函数。槽就是⼀个函数,与⼀般的C++函数是⼀样的,可以定义在类的任何位置(public、protected或private),可以具有任何参数,可以被重载,也可以被直接调用(但是不能有默认参数)。
  2. 槽函数与⼀般的函数不同的是:槽函数可以与⼀个信号关联,当信号被发射时,关联的槽函数被自动执行

说明:

(1)信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函
;每个槽也可以用函数表示,称为槽函数。例如:"按钮被按下"这个信号可以用clicked()函数表示,"窗口关闭"这个槽可以用close()函数表示,假如使用信号和槽机制-实现:"点击按钮会关闭窗口"的功能,其实就是clicked()函数调用close()函数的效果。

(2)信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:

  • 信号函数用 signals关键字修饰,槽函数用public slots、protected slots或者private slots 修饰。signals和slots是Qt在C++的基础上扩展的关键字,专门用来指明信号函数和槽函数;
  • 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)

二、信号和槽的使用

1、连接信号和槽(connect函数)

  • 在Qt中,QObject类提供了⼀个静态成员函数connect(),该函数专门用来关联指定的信号函数和槽函数
  • QObject是Qt内置的父类。Qt中提供的很多类都是直接或者间接继承自QObject
cpp 复制代码
connect (const QObject *sender, 
		 const char * signal ,
		 const QObject * receiver , 
		 const char * method , 
		 Qt::ConnectionType type = Qt::AutoConnection )
  1. QObject *sender:描述当前信号是哪个控件发出(信号的发送者)
  2. const char * signal:信号类型(发送的信号(信号函数))
  3. const QObject * receiver:哪个对象负责处理这个信号(信号的接收者)
  4. const char * method:该怎么处理(接收信号的槽函数)
  5. 最后一个Qt::ConnectionType type,暂时不考虑,用得也很少
  • 现在我们实现一个connect函数,完成以下操作:界面上有一个按钮,用户点击按钮,窗口关闭
  1. 首先创建一个按钮控件,控件显示关闭字样,且将按钮位置从左上角移动到(200,200)的位置
  2. connect函数的实现:
  • 首先传入发送信号的发送者,这里是按钮(这里传入的是QPushButton*类型,connect函数要求是QObject *,但是QPushButton继承自QObject,所以可以直接传入)
  • 发送的信号:信号也是Qt对象内部提供的成员函数。如果第一个参数是QPushButton类型,第二个参数的信号必须是QPushButton内置的信号(父类的信号),不能是其他类的信号
  • 信号接收者,这里是this
  • close是QWidget内置的槽函数,Widget继承自QWidget,也就继承了父类的槽函数
  1. 运行代码,点击按钮控件,窗口(Widget)关闭
    问题:const char * signal、const char * method,是char* 类型,但是我们传入的是一个函数指针,它们是不同类型
  • 在C++中是不允许使用两个不同的指针类型相互赋值的
  • connect函数声明是以前旧版本的函数声明,以前版本传参的写法和现在写法也不同,需要给信号参数搭配一个SIGNAL宏,给槽参数搭配一个SLOT宏。这个宏就能将传入的函数指针转换成char*
  • 旧写法:connect(button, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close))
  • Qt5开始对旧版本的写法做出了简化,不再需要写宏,给connect提供了重载版本,重载版本中第二个参数和第四个参数成了泛型参数(允许传入任意参数)

2、自定义槽函数

  • 槽本质上就是一个函数,与⼀般的C++函数是⼀样的,可以定义在类的任何位置(public、protected或private)。
  • 方式一:现在我们实现一个connect函数,界面上有一个按钮,用户点击按钮,和上面操作不一样,我们不使用内置的槽函数,而是自己写一个槽函数handleClicked,实现按下按钮,修改窗口标题
  1. 同样,首先创建按钮控件
  2. connect函数实现:
    在Qt5之前,定义槽函数必须要将槽函数放在public/protected/private slots:
  • 方式2:使用图形化界面创建按钮
  1. 图形化界面直接创建按钮
  2. 鼠标右击-->转到槽,可以看到窗口列出了QPushButton给我们提供的所有信号(包含QPushButton父类的信号),这里我们选择clicked()
  3. Qt Creater直接给我们生成好了一个函数,直接函数编写代码即可


  1. 方法二中我们会发现,我们并没用使用connect函数,但是达到了同样的效果,为什么呢?
  • 这是因为,在Qt中除了使用connect来连接信号和槽之外,还可以通过函数名字的方式来自动连接
  • 函数名void Widget::on_pushButton_clicked():on:固定前缀,pushButton这个就是我们图形化创建的按钮的名字,clicked:就是信号的名字(将pushButton这个控件的clicked信号和当前这个函数建立连接)

3、自定义信号

  • Qt中允许自定义信号,但是比较少见,实际开发中很少需要自定义信号,这里我们简单了解即可(在GUI,用户能够进行哪些操作时可以穷举的,Qt内置的信号基本已经覆盖到了上述所有可能的用户操作)
  • 信号是一个非常特殊的函数,我们只需要写出函数声明,并告诉Qt这是一个信号。函数的定义在Qt编译过程中会自动生成,我们无法干预
  • 作为信号函数,这个函数返回值必须是void,参数有没有都可以,也支持重载(后面再详细介绍)

1.定义信号和槽函数

  1. 并将槽和信号使用connect连接起来

  2. 运行我们发现,窗口标题并没有被修改。使用connect建立了连接,但是并不代表信号发出来了,信号没有发出,那么槽函数自然会不被触发

  3. 如何才能触发自定义信号?

  • Qt内置的信号不需要手动通过代码来触发,用户再GUI进行某些操作时(点击等)就会自动触发对应信号(发射信号的代码已经内置到Qt框架中)
  • 对于自定义信号,我们需要使用关键字:emit来实现自定义信号的发射,emit MySiganl();
  • emit MySiganl(),信号的发射不一定要在构造函数中,可以通过实际要求写在合适的函数中(比如按下按钮之后,改变窗口标题,那么这个发射信号就写在按钮的槽函数中,当触发按钮点击的信号时,就会发射自定义信号,从而达到效果)

4、带参数的信号和槽

  • 信号和槽可以带参数,同时也可以支持重载
  • 当信号带有参数时,槽的参数必须和信号的参数一致。(这里的一致指的是参数类型一致,个数不一致也是可以的,信号的参数个数要大于等于槽的参数)此时信号触发,调用到槽函数的时候,信号函数中的实参就能够被传递到槽函数的形参当中
  • 现在我们在上面自定义信号代码中继续补充,将自定义信号和槽传入参数

  • 传参可以起到复用代码的效果,当有多个逻辑,逻辑整体上一致,但是涉及到不同的数据时,就可以通过函数-参数来复用代码,并且不同场景中传入参数不同参数(比如当点击按钮1,窗口标题就变为"标题1",点击按钮2,窗口标题变为"标题2")

  • 信号和槽参数数量不一致情况:信号参数多余槽,能正常运行,但是槽比信号参数多,那么运行报错




  • 为什么信号和槽参数数量可以不一致呢?
  1. 一个槽函数可能会绑定多个信号
  2. 如果参数个数严格一致,绑定槽函数的要求变高
  3. 参数数量不一致允许信号和槽之间的绑定更灵活,更多的信号可以绑定到这个槽函数上
  4. 个数不一致,槽函数会按照参数顺序,拿到信号前N个参数,保证槽函数每个参数都是有值的
  • 注意:Qt中,如果想要让某个类能够使用信号槽(可以在类中定义信号和槽函数),必须要在类最后开始的地方写下Q_OBJECT宏

5、使用Lambda表达式定义槽函数

  • 如果想方便的编写槽函数,比如在编写函数时连函数名都不想定义,则可以通过Lambda表达式来达到这个目的
  • lambda表达式: ( ) {}
  • lambda表达式是一个回调函数,这个函数无法直接获得上层作用域中的变量的,因此,当我们需要每点击一次按钮,按钮就会移动到某一个位置时,无法直接获得button,为了解决这个问题lambda引入了变量捕获,通过"变量捕获"获取到了外层作用域的变量,在 中加入想要使用的变量:button, this,如果想要捕获更多的变量,可以直接使用=,这个写法的含义是将上层作用域中的所有变量都捕获

三、信号和槽断开连接

  • 使用disconnect来断开信号槽连接,使用方式和connect类似
  • 首先,我们创建2个按钮(首先按钮1绑定槽函数1,当点击按钮2之后,取消和槽函数1的绑定,将按钮1和槽函数2绑定),可以代码创建也可以图形化创建,再定义槽函数,使用connect函数连接信号和槽
  • handleClick:按钮1默认绑定的槽函数1,当点击按钮1,窗口标题修改为"修改窗口标题"
  • handleClick2:按钮1绑定的槽函数2,当按钮1与这个槽函数绑定,点击按钮1,修改窗口标题为"修改标题2"
  • on_pushButton_2_clicked:将pushButton_2这个控件的clicked信号和当前这个函数建立连接,当点击按钮2,就会执行这个槽函数,将按钮1连接的槽函数1换成槽函数2
相关推荐
YOU OU5 小时前
Redis基础常识与命令
数据库·redis·缓存
Scott9999HH5 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社6 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海6 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖7 小时前
JDK 26 新特性详解
java·开发语言
Database_Cool_7 小时前
OLTP 和 OLAP 区别详解:分析型数据库和事务型数据库怎么选(附阿里云 AnalyticDB MySQL 选型指南)
数据库·mysql·阿里云
爱写代码的阿森8 小时前
鸿蒙三方库 | harmony-utils之KvUtil键值型数据库操作详解
数据库·华为·harmonyos·鸿蒙·huawei
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
Database_Cool_8 小时前
单机 MySQL 迁移到分布式数据库方便吗?阿里云 PolarDB-X 100% MySQL 协议兼容零改造平滑迁移
数据库·分布式·mysql