【Qt】—— 信号与槽

简介: 【Qt】—— 信号与槽



(一)信号和槽概述

在Qt中,每次用户与控件互动的过程都被称为一个事件。举例来说,"用户点击按钮"是一个事件,"用户关闭窗口"也是一个事件。每个事件都会触发相应的信号,例如,当用户点击按钮时,会发出"按钮被点击"的信号;当用户关闭窗口时,会发出"窗口被关闭"的信号。这种事件和信号的机制使得在Qt应用程序中能够方便地处理用户交互和相应的行为。

在Qt中,每个控件都具备接收信号的能力,并且一个控件可以同时接收多个不同的信号。每当控件接收到信号时,它会执行相应的响应动作。举例来说,当窗口中的按钮接收到"按钮被点击"的信号时,它会执行"关闭自身"的响应动作;又如,当文本输入框接收到"文本框被点击"的信号时,它会执行"显示闪烁的光标,等待用户输入数据"的响应动作。在Qt中,对信号所做出的响应动作被称为槽函数

信号和槽是Qt独有的消息传递机制,它能够连接不同的控件并使它们相互影响。举例来说,"按钮"和"窗口"是两个独立的控件,单纯点击按钮并不会对窗口产生任何影响。通过信号和槽机制,可以将按钮和窗口连接起来,实现"点击按钮会导致窗口关闭"的效果。

1.1 信号的本质

信号是由用户对窗口或控件进行操作所触发的特定事件,这些事件会导致相应的窗口类或控件类发出特定的信号,从而对用户的操作作出响应。因此,从本质上讲,信号就是事件的一种体现

例如:

  • 按钮单击、双击
  • 窗⼝刷新
  • ⿏标移动、⿏标按下、⿏标释放
  • 键盘输⼊

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

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

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


1.2 槽的本质

(Slot)是对信号进行响应的函数。槽函数与一般的C++函数类似,可以定义在类的任何位置(public、protected或private),可以具有任意参数,可以被重载,也可以直接调用(但不能有默认参数)。

  • 与一般函数不同的是:槽函数可以与一个信号相关联。当信号被发射时,与之关联的槽函数会自动执行。

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

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

  • 信号函数用signals关键字修饰,槽函数用public slots、protected slots或private slots修饰。signals和slots是Qt在C++基础上扩展的关键字,专门用于指明信号函数和槽函数;
  • 信号函数只需声明,无需定义(实现),而槽函数需要定义(实现)。
  • 信号函数的定义是Qt⾃动在编译程序之前⽣成的.编写Qt应⽤程序的程序猿⽆需关注.
  • 这种⾃动⽣成代码的机制称为元编程(Meta Programming).这种操作在很多场景中都能⻅到.

(二)信号和槽的使用

2.1 信号和槽的连接

信号与槽的连接是Qt中用于建立信号与槽关联关系的重要机制。通过连接,一个信号可以触发一个或多个槽函数的执行,实现对象之间的通信。

在Qt中,QObject类(QObjectQt内置的⽗类.Qt中提供的很多类都是直接或者间接继承⾃QObject)提供了⼀个静态成员函数connect(),该函数专⻔⽤来关联指定的信号函数和槽函数。

connect()函数原型:

connect (const QObject *sender,
        const char * signal ,
        const QObject * receiver ,
        const char * method ,
        Qt::ConnectionType type = Qt::AutoConnection )

参数说明:

  • sender:信号的发送者;
  • signal:发送的信号(信号函数);
  • receiver:信号的接收者;
  • method:接收信号的槽函数;
  • type:⽤于指定关联⽅式,默认的关联⽅式为Qt::AutoConnection,通常不需要⼿动设定。

 


2.2 查看内置信号和槽

1、系统⾃带的信号和槽通常是通过"Qt帮助⽂档"来查询。

2、如上述⽰例,要查询"按钮"的信号,在帮助⽂档中输⼊:QPushButton

  • ⾸先可以在"Contents"中寻找关键字signals,
  • 如果没有找到,继续去⽗类中查找.因此我们去他的⽗类QAbstractButton中继续查找关键字signals

这⾥的clicked()就是要找的信号。槽函数的寻找⽅式和信号⼀样,只不过它的关键字是slot。

 

2.3 通过Qt Creator⽣成信号槽代码

Qt Creator可以快速帮助我们⽣成信号槽相关的代码。

代码示例:在窗⼝中设置⼀个按钮,当点击"按钮"时关闭"窗⼝".

1、新建项⽬,如下图为新建完成之后所包含的所有⽂件(创建时要⽣成UI设计⽂件)

2、双击widget.ui⽂件,进⼊UI设计界⾯;

3、在UI设计窗⼝中拖⼊⼀个"按钮",并且修改"按钮"的名称及字体大小等;

4、可视化⽣成槽函数;

  • 当单击"转到槽..."之后,出现如下界⾯:对于按钮来说,当点击时发送的信号是:clicked(),所以此处选择:clicked()

  • 对于普通按钮来说,使⽤ clicked 信号即可.clicked(bool) 没有意义的.具有特殊状态的按钮(⽐如复选按钮)才会⽤到 clicked(bool) .

5、⾃动⽣成槽函数原型框架;

(1)在"widget.h"头⽂件中⾃动添加槽函数的声明;

【解释说明】

⾃动⽣成槽函数的名称有⼀定的规则。槽函数的命名规则为:on_XXX_SSS,其中:

  • 1、以"on"开头,中间使⽤下划线连接起来;
  • 2、"XXX"表⽰的是对象名(控件的 objectName 属性)。
  • 3、"SSS"表⽰的是对应的信号。

如:"on_pushButton_clicked()",pushButton代表的是对象名,clicked是对应的信号。


(2)在"widget.cpp"中⾃动⽣成槽函数定义.


6、在槽函数函数定义中添加要实现的功能.实现关闭窗⼝的效果(即当我们运行代码点击关闭按钮之后窗口就会自动关闭)

 


(三)自定义信号和槽

 

3.1 基本语法

 

在Qt中,可以自定义信号和槽函数以满足特定需求。然而,自定义的信号函数和槽函数应该遵循一定的书写规范。

1、⾃定义信号函数书写规范

  • ⾃定义信号函数必须写到"signals"下;
  • 返回值为void,只需要声明,不需要实现;
  • 可以有参数,也可以发⽣重载;

2、⾃定义槽函数书写规范

  • 早期的Qt版本要求槽函数必须写到"publicslots"下,但是现在⾼级版本的Qt允许写到类的"public"作⽤域中或者全局下;
  • 返回值为void,需要声明,也需要实现;
  • 可以有参数,可以发⽣重载;

3、发送信号

  • 使⽤"emit"关键字发送信号。"emit"是⼀个空的宏。"emit"其实是可选的,没有什么含义,只是为了提醒开发⼈员。

⽰例1:

1、在widget.h中声明⾃定义的信号和槽,如图所⽰;

2、在widget.cpp中实现槽函数,并且关联信号和槽

注意:图中的①和②的顺序不能颠倒

原因是,⾸先关联信号和槽,⼀旦检测到信号发射之后就会⽴⻢执⾏关联的槽函数。反之,若先发射信号,此时还没有关联槽函数,当信号发射之后槽函数不会响应.


3.2 带参数的信号和槽

Qt的信号和槽也⽀持带有参数,同时也可以⽀持重载.

此处我们要求,信号函数的参数列表要和对应连接的槽函数参数列表⼀致.

此时信号触发,调⽤到槽函数的时候,信号函数中的实参就能够被传递到槽函数的形参当中.

(💡 通过这样的机制,就可以让信号给槽传递数据了.)

示例:重载信号槽

(1)在"widget.h"头⽂件中声明重载的信号函数以及重载的槽函数;如下图所⽰:

(2)在"Widget.cpp"⽂件实现重载槽函数以及连接信号和槽。

  • 需要注意的是:在定义函数指针时要指明函数指针的作⽤域。

(3)执⾏结果如下图所⽰:

 


(四)信号与槽的连接⽅式

4.1 ⼀对⼀

主要有两种形式,分别是:⼀个信号连接⼀个槽⼀个信号连接⼀个信号

(1)⼀个信号连接⼀个槽

 

代码⽰例:

1、在"widget.h"中声明信号和槽以及信号发射函数;

 

2、在"widget.cpp"中实现槽函数,信号发射函数以及连接信号和槽;

(2)⼀个信号连接另⼀个信号

代码示例:

在上述⽰例的基础上,在"widget.cpp"⽂件中添加如下代码:

 


4.2 ⼀对多

⼀个信号连接多个槽

⽰例:

(1)在"widget.h"头⽂件中声明⼀个信号和三个槽;

(2)在"widget.cpp"⽂件中实现槽函数以及连接信号和槽;

 

 


4.3 多对⼀

多个信号连接⼀个槽函数

⽰例:

(1)在"widget.h"头⽂件中声明两个信号以及⼀个槽;

(2)在"widget.cpp"⽂件中实现槽函数以及连接信号和槽;


(五)信号和槽的其他说明

5.1 信号与槽的断开

在Qt中,可以使用 disconnect 函数来断开信号与槽之间的连接。disconnect 函数允许你在运行时动态地断开连接,以便停止两个对象之间的信号和槽的关联。(disconnect的⽤法和connect基本⼀致.)

代码示例:

  • 需要注意的是,断开连接时,必须提供与建立连接时相同的参数,包括信号函数和槽函数的指针或函数指针。

5.2 Qt4版本信号与槽的连接

Qt4中的connect⽤法和Qt5相⽐是更复杂的.需要搭配 SIGNAL 和 SLOT 宏来完成。⽽且缺少必要的函数类型的检查,使代码更容易出错。

代码示例:

(1)在"widget.h"头⽂件中声明信号和槽

(2)在"widget.cpp"⽂件中实现槽函数以及连接信号与槽;

Qt4版本信号与槽连接的优缺点:

  • 优点:参数直观;
  • 缺点:参数类型不做检测;

代码示例:


5.3 使⽤Lambda表达式定义槽函数

Qt5在Qt4的基础上提⾼了信号与槽的灵活性,允许使⽤任意函数作为槽函数。

但如果想⽅便的编写槽函数,⽐如在编写函数时连函数名都不想定义,则可以通过Lambda表达式来达到这个⽬的。

在Qt中,可以使用Lambda表达式来定义槽函数,这使得代码更加简洁和可读。Lambda表达式是C++11引入的一种匿名函数形式,允许我们在需要函数的地方内联定义函数。

下面是一个使用Lambda表达式定义槽函数的示例:

// 假设有一个QPushButton对象 btn
connect(btn, &QPushButton::clicked, [=]() {
    qDebug() << "按钮被点击了";
    // 在这里写下按钮被点击时需要执行的操作
});

【解释说明】

  • 在这个示例中,我们将按钮的clicked信号与一个Lambda表达式连接起来。
  • Lambda表达式作为槽函数,使用了方括号[]来捕获外部变量,这里使用了捕获所有外部变量的方式[=]。
  • Lambda表达式中的代码将会在按钮被点击时执行。

5.4 信号与槽的优缺点

优点:松散耦合

  • 信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了⾃⼰,Qt的信号槽机制保证了信号与槽函数的调⽤。⽀持信号槽机制的类或者⽗类必须继承于QObject类。

缺点:效率较低

  • 与回调函数相⽐,信号和槽稍微慢⼀些,因为它们提供了更⾼的灵活性,尽管在实际应⽤程序中差别不⼤。
  • 通过信号调⽤的槽函数⽐直接调⽤的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调⽤速度对性能要求不是⾮常⾼的场景是可以忽略的,是可以满⾜绝⼤部分场景。

总结

在Qt中,信号与槽是一种强大的通信机制,用于在对象之间进行异步通信。以下是关于信号与槽的简要小结:

  1. 信号
  • 信号是Qt中特有的概念,是一种特殊的成员函数,用于通知其他对象发生了某种特定的事件。
  • 信号由signals:关键字声明,在类的声明部分中定义。
  • 信号函数通常不包含实际的实现,只是用来发出信号。
  • 信号函数可以有参数,参数的类型必须是Qt元对象系统支持的数据类型。
  • 槽是用于响应信号的函数,可以执行特定的操作以响应信号的发生。
  • 槽函数由slots:关键字声明,在类的声明部分中定义。
  • 槽函数可以是普通成员函数、静态成员函数或者Lambda表达式。
  • 槽函数的参数类型必须与连接的信号的参数类型匹配。
  1. 连接
  • 连接是指建立信号与槽之间的关联,使得当信号被发出时,相关的槽函数会被调用。
  • 连接通过QObject::connect()函数来实现,可以连接两个QObject对象之间的信号和槽。
  • 在连接时,需要指定发送信号的对象、信号函数、接收信号的对象以及槽函数。
  • 连接还可以使用Qt 5中引入的新语法,使得连接更加类型安全。
  1. 自定义信号与槽
  • Qt允许自定义信号和槽函数,以满足特定需求。
  • 自定义的信号函数和槽函数应该遵循一定的书写规范,例如在类的声明部分中使用signals:slots:关键字声明。
  • 自定义的信号函数和槽函数可以有参数,参数的类型必须是Qt元对象系统支持的数据类型。

使用信号与槽机制可以实现对象之间的松耦合通信,使得代码更加模块化、可维护和可扩展。

相关文章
|
1月前
|
编译器
(9)Qt中信号与槽重载的解决方案
本文介绍了在Qt中处理信号与槽重载问题的三种解决方案:使用函数指针、Qt提供的QOverload类和Qt4的宏方式。
89 3
|
6月前
|
存储 安全 编译器
【Qt 底层机制之信号和槽 】深入探究Qt信号和槽背后的原理
【Qt 底层机制之信号和槽 】深入探究Qt信号和槽背后的原理
1911 4
|
6月前
【Qt 学习笔记】按钮实现helloworld | 信号与槽概述
【Qt 学习笔记】按钮实现helloworld | 信号与槽概述
82 0
|
6月前
|
编译器 C++ 开发者
QT基础【7-跨进程发送信号】
QT基础【7-跨进程发送信号】
|
6月前
|
存储 API C++
【Qt 信号槽】深入探索 Qt 信号和槽机制中的引用传递“ (“A Deep Dive into Reference Passing in Qt Signal and Slot Mechanism“)
【Qt 信号槽】深入探索 Qt 信号和槽机制中的引用传递“ (“A Deep Dive into Reference Passing in Qt Signal and Slot Mechanism“)
509 0
|
6月前
|
安全 编译器 开发者
【Qt 学习笔记】Qt信号和槽的其他说明及Lambda表达式
【Qt 学习笔记】Qt信号和槽的其他说明及Lambda表达式
202 0
|
1月前
(8)Qt中的自定义信号
本文介绍了如何在Qt框架中创建和使用自定义信号,并通过一个父子窗口切换的示例来展示自定义信号的实现和应用。
61 3
(8)Qt中的自定义信号
|
10天前
|
C++
003 Qt_信号和槽-上
本文介绍了Qt中的信号与槽机制,包括信号和槽的概念、本质及连接方法,并演示了如何自定义槽函数。信号是事件的体现,槽是对信号的响应函数。通过信号与槽,可以将独立的控件关联起来,实现复杂的交互逻辑。文中还详细展示了如何在Qt项目中定义和使用槽函数,通过实例代码和图形化界面操作,帮助读者更好地理解和应用这一机制。
20 1
003 Qt_信号和槽-上
|
5月前
|
安全 C++ Windows
Qt信号与槽机制
Qt信号与槽机制
50 1
|
3月前
|
程序员 C++
【Qt】信号与槽(下)
【Qt】信号与槽(下)
下一篇
无影云桌面