Qt 信号槽的使用

5/30/2022 Qt

# 简介

信号槽是Qt的一种特殊机制。类似于回调

当连接信号与槽之后,当一边发送信号后,与之连接的槽函数就会执行

信号与槽的返回类型与参数数量,类型一致

signals:
	void ShowInfoSignal(QString); // 不需要实现

slots:
    void ShowInfoSlot(QString); // 需要实现
1
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
#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

如上面的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

connect有5个参数

  1. 信号发送者
  2. 发送的信号
  3. 接受者(槽函数对象)
  4. 槽函数
  5. 连接的方式(直连,还是跨线程。。)

这种方式需要把信号与槽函数分别写在SIGNAL()与SLOT()的宏定义里,并且参数只需要写类型,不能写变量名

# 2

一般情况下,,这么写就可以了

connect(this, &Widget::InfoSignal, mShowInfo.get(), &ShowInfo::ShowInfoSlot);
connect(this, &Widget::InfoSignal, mShowInfo.get(), &ShowInfo::ShowInfoSlot);
1
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

# 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

指向具体的函数

	// 同样的因为重载,需要显示指定一下,一般不需要
	void (ShowInfo::*slot1)(QString) = &ShowInfo::ShowInfoSlot;
    showSlot1 = slot1;
1
2
3

使用

	// 因为函数指针只是指向一个函数,所以还需要对应的类的实例去调用它。
	(mShowInfo.get()->*showSlot1)("from signal 1");
1
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
    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
#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