信号和槽

信号和槽本质上是两个类在进行通信。
信号或是传递值,或者是传递动作变化,槽函数响应信号或是接受值,或者根据动作变化来做出对应操作。
信号本质
信号是由于用户对窗口或者控件做了操作,导致窗口或者控件产生了某个特定事件,这时候对应的窗口类会发出某个信号,以此对用户的挑选做出反应。
信号是qt对象中特殊的函数,本质上是一种函数指针,事件发生时调用,不是在信号所属的对象中直接执行。
信号的呈现形式就是函数,也就是说某个事件产生了,qt框架会调用某个对应的信号函数,通知使用者。
槽的本质
槽的职责是对qt框架中产生的信号进行处理。
槽是qt对象中普通成员函数,用于处理信号发生时的动作。一个槽可以连接一个或者多个信号,当信号触发时,与之连接的槽会被调用。

信号和槽机制优点

①类型安全:需要关联的信号和槽的签名必须是等同的。

②松散耦合:槽和信号不需要知道对方(信号不用知道是哪个槽接收的,槽不用知道哪些信号关联自己)

连接
连接是信号和槽之间的桥梁,它将一个信号与一个槽相关联。当信号发生时,连接会调用与之关联的槽。连接的建立通常通过函数实现。

信号与槽关联是用 QObject::connect() 函数实现的,其基本格式是:

1
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

connect() 是 QObject 类的一个静态函数,而 QObject 是所有 Qt 类的基类,在实际调用
时可以忽略前面的限定符,所以可以直接写为:

1
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

其中, sender 是发射信号的对象的名称, signal() 是信号名称。信号可以看做是特殊的函数,需要带括号,有参数时还需要指明参数。 receiver 是接收信号的对象名称, slot() 是槽函数的名称,需要带括号,有参数时还需要指明参数。
SIGNAL 和 SLOT 是 Qt 的宏,用于指明信号和槽,并将它们的参数转换成为相应的字符串。
###创建一个UI项目


点击mainwindow.ui文件进入ui界面设计地步。
ui界面英文含义

Layouts 布局
vertical Layouts 垂直布局
Horizontal Layouts 水平布局
grid Layouts 网格布局
form Layouts 表格布局
Horizontal spacer 水平部件
vertical spacer 垂直部件
push button 下压按钮
tool button 工具按钮
radio button 选择按钮
check button 复选按钮
command check button 命令链接按钮
dialog button box 对话框按钮
list view 列表视图
tree view 树视图
table view 表视图
column view 列视图
undo view 撤销视图
widget 控件
显示hello world,把控件中的文本控件拽出,并打字hello world就可以编译运行了。
创建一个简单的UI按键项目
将控件push button 拿出,打字关闭程序,这时候就要将控件以及信号槽连接。
选择按钮的 clicked()信号,将其连接 MainWindow 对象的 close()槽。这样就完成了信号与槽的连接。
在使用信号与槽的类中,必须在类的定义中加入宏 Q_OBJECT(特别重要)。
当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。
只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。
总结如下图, 可以看到发送者与发送的信号是在一起的,接收者与接收的信号/槽是在一起的。 它们不能在 connect()方法里写乱顺序! 由发送者发送出信号到接收者用信号/槽接收。

如何在项目中创建槽
创建槽的方法也很简单, 也是直接在 mianwindow.h 里直接声明槽,在 mianwindow.cpp 里实现槽的定义, 声明槽必须写槽的定义(定义指函数体的实现),否则编译器编译时将会报错。
槽有以下特点:

  1. 槽可以是任何成员函数、普通全局函数、静态函数

  2. 槽函数和信号的参数和返回值要一致
    mainwindow.h 添加槽函数后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
/* 引入 QPushButton */
#include <QPushButton>

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;
};
#endif

在 mainwindow.cpp 里 实 现 声 明 的 槽 函 数 void changeButtonText(); 和 voidpushButtonClicked();。 同时还实例化了 pushButton 对象。代码如下。
mainwindow.cpp 添加槽的实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{
/* 设置窗体的宽为 800,高为 480 */
this->resize(800,480);

/* 实例化 pushButton 对象 */
pushButton = new QPushButton(this);

/* 调用 setText()方法设定按钮的文本 */
pushButton->setText("我是一个按钮");
}

MainWindow::~MainWindow()
{

}

/* 实现按钮点击槽函数 */
void MainWindow::pushButtonClicked()
{
/* 使用 emit 发送信号 */
emit pushButtonTextChanged();
}

/* 实现按钮文本改变的槽函数 */
void MainWindow::changeButtonText()
{
/* 在槽函数里改变按钮的文本 */
pushButton->setText("被点击了! ");
}

如何在项目中连接信号与槽

1
2
connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
connect(this, SIGNAL(pushButtonTextChanged()), this, SLOT(changeButtonText()));

注意,发送信号的对象,和接收的信号的对象。 因为我们 pushButtonClicked()是本类里定义的槽,所以用 this 来接收。同理, pushButtonTextChanged()也是本类定义的信号。所以发送者写成 this。 changeButtonText()也是本类的槽函数,所以接收槽的对象也是 this。

在 mainwindow.cpp 中信号槽连接的代码如下。
mainwindow.cpp 实现连接信号槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{
/* 设置窗体的宽为 800,高为 480 */
this->resize(800,480);

/* 实例化 pushButton 对象 */
pushButton = new QPushButton(this);

/* 调用 setText()方法设定按钮的文本 */
pushButton->setText("我是一个按钮");

/* 信号与槽连接 */
connect(pushButton, SIGNAL(clicked()), this,SLOT(pushButtonClicked()));

connect(this, SIGNAL(pushButtonTextChanged()), this,SLOT(changeButtonText()));
}

MainWindow::~MainWindow()
{

}

/* 实现按钮点击槽函数 */
void MainWindow::pushButtonClicked()
{
/* 使用 emit 发送信号 */
emit pushButtonTextChanged();
}

/* 实现按钮文本改变的槽函数 */
void MainWindow::changeButtonText()
{
/* 在槽函数里改变按钮的文本 */
pushButton->setText("被点击了! ");
}