Qt信号与槽

1.What-什么是Qt信号与槽机制

​ 信号与槽是Qt特有的消息传输机制,它可以将两个独立的对象关联起来。

​ 举一个简单的例子,当手机到了一条短信,就会发出消息提示音。原本的短信和手机提示音没有什么关系,但是,你的手机设置了当收到短信的时候就发出消息提示音,将两者绑定在一起。所以,此时,手机的操作系统定义了一个信号(signal),表示收到了新的短信,当有新的短信到达的时候,这个信号就会被触发,手机将短信和消息提示音连接(connect)起来后,就会自动触发其绑定的消息提示音,即槽(slot)函数被调用,进而触发一个提示音来提醒你有新的消息。也就是说,当信号发出时,被连接的槽函数会自动被回调。

​ 信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。

​ 在Qt中,信号(signal) 相当于发出一个事件通知,其本质是一种特殊类型的函数,用于在特定事件发生时向其他对象发送通知。

​ 在Qt中,槽(Slot) 是一个函数,为了响应信号而执行的一系列的指令。槽函数在特定事件发生时被触发执行。槽的本质 是类的成员函数,其参数可以是任意类型的。和普通C++成员函数几乎没有区别,它可以是虚函数;也可以被重载;可以是公有的(public)、保护的(protected)、私有的(private)、也可以被其他C++成员函数调用。唯一区别的是:槽可以与信号连接在一起,每当和槽连接的信号被发射的时候,就会调用这个槽。

连接(connect) 是我们指定什么样的信号执行什么样的槽函数。

2.How-如何创建信号与槽

2.1如何创建信号

​ 在Qt中,信号只需声明,无需定义,所以,我们只需要在相应的.h文件中signals下声明信号即可。需要注意的是,定义信号最好就是贴近信号本身的含义。自定义的信号可以有参数,也可以重载。

​ 举例,在这里创建一个 void pushButtonTextChanged(); 信号:

//在.h文件中添加信号后的代码。
class MainWindow : public QMainWindow
{
 		Q_OBJECT
 public:
 		MainWindow(QWidget *parent = nullptr);
 		~MainWindow();
 signals:
 /* 声明一个信号,只需声明,无需定义 */
 		void pushButtonTextChanged();
 };

如果想要使用自定义的信号, 首先要编写新的类并且让其继承Qt的某些标准类,我们自己编写的类想要在Qt中使用使用信号槽机制, 那么必须要满足的如下条件:

  1. 这个类必须从QObject类或者是其子类进行派生

  2. 在定义类的头文件中加入 Q_OBJECT 宏

要求:

  1. 信号是类的成员函数
  2. 返回值是 void 类型
  3. 信号的名字可以根据实际情况进行指定
  4. 参数可以随意指定, 信号也支持重载 void signal2(int, char);
  5. 信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字
  6. 信号函数只需要声明
  7. 在程序中发送自定义信号: 发送信号的本质就是调用信号函数

2.2如何创建槽

​ 在Qt中,自定义槽函数,需要声明也需要实现。所以,在**.h文件中写到 public slot 下或 public 或者全局函数中,并在相应的.cpp文件中进行实现**,否则编译器编译时将会报错。自定义的槽可以有参数,也可以重载。

槽有以下特点:

  1. 槽可以是任何成员函数、普通全局函数、静态函数、Lambda表达式
  2. 槽函数参数的类型和个数需要和对应的信号对应起来,参数个数可以比信号少,但不能多。

根据上面的槽特点,由于我们在 2.1小节里声明了信号 void pushButtonTextChanged(); 所以我们声明的槽函数必须是无返回值类型 void和无需参数。所以声明槽的代码如下。此外 我们还声明一个 QPushButton 对象 pushButton。

在.h文件中

//在.h文件中添加信号后的代码。
class MainWindow : public QMainWindow
{
 		Q_OBJECT
 public:
 		MainWindow(QWidget *parent = nullptr);
 		~MainWindow();
 signals:
 /* 声明一个信号,只需声明,无需定义 */
 		void pushButtonTextChanged();
 public slots:
 /* 声明一个槽函数 */
 		void changeButtonText();

 /* 声明按钮点击的槽函数 */
 		void pushButtonClicked();
 private:
 /* 声明一个对象 pushButton */
 		QPushButton *pushButton;
};

在.cpp文件中:

//.cpp 中添加槽的实现代码,实现声明的槽函数 void changeButtonText(); 和 
//void pushButtonClicked(); 同时还实例化了 pushButton 对象。
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{
/* 设置窗体的宽为 800,高为 480 */
		this->resize(800,480);
/* 实例化 pushButton 对象,在堆中实例化,并指定父对象为this */
 		pushButton = new QPushButton(this);
 /* 调用 setText()方法设定按钮的文本 */
 		pushButton->setText("我是一个按钮");
 }

 MainWindow::~MainWindow()
 {
 }
 /* 实现按钮点击槽函数 */
 void MainWindow::pushButtonClicked()
 {
 /* 使用 emit 发送信号 */
 		emit pushButtonTextChanged();
 }
 /* 实现按钮文本改变的槽函数 */
 void MainWindow::changeButtonText()
 {
 /* 在槽函数里改变按钮的文本 */
 		pushButton->setText("被点击了!");
 }

​ 如果是触发自定义的信号,有个关键函数是“emit”。触发了信号之后,就会自动找connect()与其连接的函数去实现。

其中,上述代码有一句比较难理解:MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) 这是初始化基类的数据成员变量的方法。说一下,这种代码的具体格式

派生类::派生类构造函数(总参数列表):基类构造函数(参数列表) //基类构造函数的参数列表是实参。
{

派生类中的数据成员初始化;

}

​ 若对此问题感兴趣,可参考文章:

文章1

文章2

3.How-如何在项目中连接信号与槽

​ 信号与槽之间的关联是通过connect()函数来实现的。

注意:只有QObject类及其派生类才能使用信号和槽机制。 当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外。主要分成两类:1、Qt类自带的 2、用户自定义的。

信号槽连接的代码如下。

connect函数原型:
//Qt5版本之前:
QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

//Qt5版本之后:
QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)

简化即connect(from,what,to,do);

​ 需要注意的是,如果发送的对象和接受的对象是本类定义的信号和槽函数,两者均可以用“this”来替代,即“from” 和“to”的位置均可以用“this”来替代。

​ 断开信号的话,就是使用disconnect()函数。

connect()函数用法:

第一种:Qt4使用宏,主要通过connect+宏的方式进行通信连接

connect()函数常用的语法格式是:

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

各个参数的含义分别是:

  • sender:指定信号的发送者;
  • signal:指定信号函数,信号函数必须用 SIGNAL() 宏括起来;
  • reveiver:指定信号的接收者;
  • method:指定接收信号的槽函数,槽函数必须用 SLOT() 宏括起来;
  • type 用于指定关联方式,默认的关联方式为 Qt::AutoConnection,通常不需要手动设定。

connect(发送对象,信号,接收对象,槽函数),其中发送信号和槽函数需要用 SIGNAL() 和 SLOT() 来进行明确的声明。

以下示例先自定义一个 Button,然后定义两个信号:

class MyButton : public QWidget
{
Q_OBJECT
public:
  explicit MyButton(QWidget *parent = nullptr);

signals:
  void sigClicked();
  void sigClicked(bool check); //重载信号
};

那么在用这个 Button 的时候连接这两个信号,按照旧版本的写法,应该是这样:

connect(m_pBtn,SIGNAL(sigClicked()),this,SLOT(onClicked()));
connect(m_pBtn,SIGNAL(sigClicked(bool)),this,SLOT(onClicked(bool)));

此法优点:参数直观

此法缺点:参数类型不做检测

第二种:Qt 5 推出了新的 connect 函数,不需要使用 SIGNAL() 和 SLOT() 宏,可以在编译时做类型检查:

connect函数原型如下:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)

即connect(信号的发送者,信号发送端(函数的地址),信号的接收者,信号的处理(即槽函数地址));

​ 用 connect 将信号与槽函数连接,不需要再使用 SIGNAL() 和 SLOT() 宏。而且使用这种方法槽函数的声明不需要放到slots中,只需要像普通的函数一样声明就可以了。

举例:

connect( myBtn , &QPushButton::clicked , this, &QWidget::close );

需要注意的是, 第二个参数还有第四个参数都要放函数的地址。现在实际上可以不需要用‘&’,因为现在函数的名称本身就是函数的地址,但是由于早期的版本中,想要取函数的地址必须要‘&’这个符号。

现在按照规范来写的话,(第二、第四个函数)若是成员函数的地址的话,要加上作用域(QPushButton、QWidget),告诉其成员函数地址(clicked、close),最后在其前面加个‘&’。这样比较清楚点。其语法格式为 &+函数所在类+函数名

​ 前面版本只是适用于没有重载的情况下,但如果是信号或者槽发生了重载,写的函数地址就不明确了,所以就需要函数指针来指明其函数地址,使用函数指针可以明确告诉编译器使用哪一个重载函数,避免歧义。

举例:

void(Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
void(Student:: *studentSlot)(QString)   = &Student::treat;
connect(zt,teacherSignal,st,studentSlot);

信号槽的优点是松散耦合,信号的发送端和接收端本身是没有关联的,通过connect连接,将两端耦合在一起。

需要注意:(信号与槽是独立的对象)

​ 1.信号可以连接信号,当信号发出时,会随之发出另一个信号(中间的信号作为中继点),因为connect()函数中的method参数还可以指定一个信号函数

​ 2.一个信号可以连接多个槽函数(手机短信来临可以消息提示音,可以手机震动)

​ 3.多个信号可以连接同一个槽函数(手机短信、APP应用消息、邮件来临均可以设置出现消息提示音)

​ 4.信号和槽函数的参数,类型必须一一对应

​ 5.信号和槽的参数的个数可以不一致,但信号的参数个数必须大于或等于槽函数的参数个数并且参数的位置也要对应

​ 6.槽函数可以使用lambda表达式

​ 7.信号槽连接之后,可以被断开,使用disconnect()函数

4.How-如何使用Qt类的信号与槽(帮助文档)

​ Qt里面有大量的信号与槽,都是Qt自定义好的,功能强大,基本上够我们使用了。想要使用Qt里面的信号和槽,可以打开Qt自带的帮助文档。如何使用搜索出来的帮助文档呢?比如要查看 QPushButton 类有哪些属性和函数可以用可以查看“Properties”和“Public Functions”这个标题下的属性和函数,如下图。可以看到 QPushButton 类有如下属性和函数可以使用。

在这里插入图片描述

​ 再比如,假若我们不知道这个 QPushButton 类有哪些可用的槽,我们可以继续往下查看,找到 Public Slots 即可!我们查看某个方法用法时,直接单击绿色单词即可进入查看某个方法的用法即可!

在这里插入图片描述

​ 再比如你想要了解QPushButton的基本信息的时候,就可以找到“QPushButton Class”,里面包含了其所在的头文件、qmake中所需要添加的语句、其继承(Inherits)自哪个类等信息。

在这里插入图片描述

​ 当不断深入了解其中的内容的时候,想要返回上一级或下一级就可以使用快捷键了。使用的快捷键(Alt + ←(方向左键))返回到上一级,或者进入下一级(Alt + → (方向右键))了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值