Qt 信号槽的使用
furrain 5/30/2022 Qt
# 简介
信号槽是Qt的一种特殊机制。类似于回调
当连接信号与槽之后,当一边发送信号后,与之连接的槽函数就会执行
信号与槽的返回类型与参数数量,类型一致
signals:
void ShowInfoSignal(QString); // 不需要实现
slots:
void ShowInfoSlot(QString); // 需要实现
1
2
3
4
5
2
3
4
5
# 两种连接方式
# 1
#include <QWidget>
#include <ShowInfo.h>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QSharedPointer<ShowInfo> mShowInfo;
void (ShowInfo::*showSlot1)(QString) = nullptr;
std::function<void(QString, QString)> showShot2 = nullptr;
signals:
void InfoSignal(QString info);
void InfoSignal(QString info1, QString info2);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
};
#endif // WIDGET_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
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
#ifndef SHOWINFO_H
#define SHOWINFO_H
#include <QWidget>
namespace Ui {
class ShowInfo;
}
class ShowInfo : public QWidget
{
Q_OBJECT
public:
explicit ShowInfo(QWidget *parent = nullptr);
~ShowInfo();
public slots:
void ShowInfoSlot(QString info);
void ShowInfoSlot(QString info1, QString info2);
private:
Ui::ShowInfo *ui;
};
#endif // SHOWINFO_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
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
如上面的h所示,有两个信号槽对应
connect(this, SIGNAL(InfoSignal(QString)), mShowInfo.get(), SLOT(ShowInfoSlot(QString)));
connect(this, SIGNAL(InfoSignal(QString, QString)), mShowInfo.get(), SLOT(ShowInfoSlot(QString, QString)));
1
2
2
connect有5个参数
- 信号发送者
- 发送的信号
- 接受者(槽函数对象)
- 槽函数
- 连接的方式(直连,还是跨线程。。)
这种方式需要把信号与槽函数分别写在SIGNAL()与SLOT()的宏定义里,并且参数只需要写类型,不能写变量名
# 2
一般情况下,,这么写就可以了
connect(this, &Widget::InfoSignal, mShowInfo.get(), &ShowInfo::ShowInfoSlot);
connect(this, &Widget::InfoSignal, mShowInfo.get(), &ShowInfo::ShowInfoSlot);
1
2
2
但是你发现,在信号或者槽函数有重载情况发生时,这就会报错,因为这种方式不需要写参数类型,他分不清你具体指的是哪一个。
在上面的特殊例子中,会发现两者一样,就傻眼了
所以,当信号或者槽 有重载时,就麻烦一点了。需要用函数指针或者显式声明强转,告诉他你指的具体是哪一个
// 用函数指针
void (Widget::*singal1)(QString) = &Widget::InfoSignal;
void (ShowInfo::*slot1)(QString) = &ShowInfo::ShowInfoSlot;
connect(this, singal1, mShowInfo.get(), slot1);
// 用显式强转
connect(this, static_cast<void(Widget::*)(QString, QString)>(&Widget::InfoSignal), mShowInfo.get(),
static_cast<void(ShowInfo::*)(QString, QString)>(&ShowInfo::ShowInfoSlot));
1
2
3
4
5
6
7
2
3
4
5
6
7
# 3
所以第一种方式更加安全一点,任何情况下,他都能准确知道你指的是哪个信号,哪个槽函数
但是第二种也不是一无是处,至少,当你在使用lambda时,会方便一点
connect(this, &Widget::xxxxx, mShowInfo.get(), [](QString){});
1
当你不想为槽函数单独写一个函数时,这会很方便
# 回调
其实信号槽也是特殊的回调,那么回调改怎么写呢
# 1.函数指针
声明函数指针
void (ShowInfo::*showSlot1)(QString) = nullptr;
// 或者
typedef void (ShowInfo::*Fun)(QString);
Fun showSlot1 = nullptr;
1
2
3
4
2
3
4
指向具体的函数
// 同样的因为重载,需要显示指定一下,一般不需要
void (ShowInfo::*slot1)(QString) = &ShowInfo::ShowInfoSlot;
showSlot1 = slot1;
1
2
3
2
3
使用
// 因为函数指针只是指向一个函数,所以还需要对应的类的实例去调用它。
(mShowInfo.get()->*showSlot1)("from signal 1");
1
2
2
# 2.std::function<>()
std::function<void(QString, QString)> showShot2 = nullptr;
1
void (ShowInfo::*slot2)(QString, QString) = &ShowInfo::ShowInfoSlot;
showShot2 = std::bind(slot2, mShowInfo.get(), std::placeholders::_1, std::placeholders::_2);
1
2
2
showShot2(from signal 2", "hello");
1
# 全部cpp(.h在前面)
#include "Widget.h"
#include "ui_Widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
mShowInfo = QSharedPointer<ShowInfo>(new ShowInfo);
mShowInfo->show();
// way 1
// connect(this, SIGNAL(InfoSignal(QString)), mShowInfo.get(), SLOT(ShowInfoSlot(QString)));
// connect(this, SIGNAL(InfoSignal(QString, QString)), mShowInfo.get(), SLOT(ShowInfoSlot(QString, QString)));
// way 2
// void (Widget:: *singal1)(QString) = &Widget::InfoSignal;
// void (ShowInfo::*slot1)(QString) = &ShowInfo::ShowInfoSlot;
// connect(this, singal1, mShowInfo.get(), slot1);
// connect(this, static_cast<void(Widget::*)(QString, QString)>(&Widget::InfoSignal), mShowInfo.get(),
// static_cast<void(ShowInfo::*)(QString, QString)>(&ShowInfo::ShowInfoSlot));
void (ShowInfo::*slot1)(QString) = &ShowInfo::ShowInfoSlot;
// showSlot1 = slot1;
fun1 = slot1;
void (ShowInfo::*slot2)(QString, QString) = &ShowInfo::ShowInfoSlot;
showShot2 = std::bind(slot2, mShowInfo.get(), std::placeholders::_1, std::placeholders::_2);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// InfoSignal("from signal 1");
// (mShowInfo.get()->*showSlot1)("from signal 1");
(mShowInfo.get()->*fun1)("55555");
}
void Widget::on_pushButton_2_clicked()
{
// InfoSignal("from signal 2", "hello");
showShot2("???", "!!!1");
}
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
38
39
40
41
42
43
44
45
46
47
48
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
38
39
40
41
42
43
44
45
46
47
48
#include "ShowInfo.h"
#include "ui_ShowInfo.h"
ShowInfo::ShowInfo(QWidget *parent) :
QWidget(parent),
ui(new Ui::ShowInfo)
{
ui->setupUi(this);
}
ShowInfo::~ShowInfo()
{
delete ui;
}
void ShowInfo::ShowInfoSlot(QString info)
{
ui->label->setText(info);
}
void ShowInfo::ShowInfoSlot(QString info1, QString info2)
{
ui->label_2->setText(info1 + "--->" +info2);
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25