0
点赞
收藏
分享

微信扫一扫

【Qt】—— 信号与槽

ixiaoyang8 2024-03-17 阅读 25

目录

(一)信号和槽概述

1.1 信号的本质

1.2 槽的本质

 (二)信号和槽的使用

2.1 信号和槽的连接

2.2 查看内置信号和槽

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

(三)自定义信号和槽

3.1 基本语法

3.2 带参数的信号和槽

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

4.1 ⼀对⼀

4.2 ⼀对多

4.3 多对⼀

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

5.1 信号与槽的断开

5.2 Qt4版本信号与槽的连接

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

5.4 信号与槽的优缺点

 总结


(一)信号和槽概述

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

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

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

1.1 信号的本质

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

例如:

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

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

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

1.2 槽的本质

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

  • 与一般函数不同的是:槽函数可以与一个信号相关联。当信号被发射时,与之关联的槽函数会自动执行。
  • 信号函数的定义是Qt⾃动在编译程序之前⽣成的.编写Qt应⽤程序的程序猿⽆需关注.
  • 这种⾃动⽣成代码的机制称为元编程(Meta Programming).这种操作在很多场景中都能⻅到.

 (二)信号和槽的使用

2.1 信号和槽的连接

在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 基本语法
 

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

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

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

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

3、发送信号

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

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

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


3.2 带参数的信号和槽

示例:重载信号槽
(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 信号与槽的断开

代码示例:

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

5.2 Qt4版本信号与槽的连接

代码示例:

(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表达式。
    • 槽函数的参数类型必须与连接的信号的参数类型匹配。
  2. 连接

    • 连接是指建立信号与槽之间的关联,使得当信号被发出时,相关的槽函数会被调用。
    • 连接通过QObject::connect()函数来实现,可以连接两个QObject对象之间的信号和槽。
    • 在连接时,需要指定发送信号的对象、信号函数、接收信号的对象以及槽函数。
    • 连接还可以使用Qt 5中引入的新语法,使得连接更加类型安全。
  3. 自定义信号与槽

    • Qt允许自定义信号和槽函数,以满足特定需求。
    • 自定义的信号函数和槽函数应该遵循一定的书写规范,例如在类的声明部分中使用signals:slots:关键字声明。
    • 自定义的信号函数和槽函数可以有参数,参数的类型必须是Qt元对象系统支持的数据类型。

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

举报

相关推荐

0 条评论