Qt中的信号与槽

1:信号与槽概述

1.1:信号的本质

1.2:槽的本质

2:信号与槽的使用

2.1:连接信号与槽

2.1.1:纯代码方式

2.1.1.1:widget.h

2.1.1.2:widget.cpp

2.1.1.3:main.cpp

2.1.2:图形化方式

2.1.2.1:widget.h

2.1.2.2:widget.cpp

2.1.2.3:main.cpp

2.2:查看内置信号与槽

[2.3:通过Qt Creator生成槽代码](#2.3:通过Qt Creator生成槽代码)

2.3.1:First

2.3.2:Second

2.3.3:Third

2.3.4:Fourth

2.3.5:Fifth

2.3.6:Sixth

3:自定义信号与槽

3.1:自定义槽函数

3.1.1:语法

3.1.2:代码示例1

First:在widget.h声明自定义的槽函数

Second:在widget.cpp中实现槽函数,并且关联信号与槽

Third:运行结果

widget.h

widget.cpp

main.cpp

3.1.3:代码示例2

First:点击widget.ui文件,并且拖拽控件

Second:右击输入框,并选择转到槽​编辑

Third:编写自定义槽函数

Forth:运行结果

widget.h

widget.cpp

main.cpp

3.1.4:小结

3.2:自定义信号

3.2.1:基本语法

3.2.2:发送信号

3.2.3:代码示例

[First:在 widget.h 中声明自定义的信号,如图所示](#First:在 widget.h 中声明自定义的信号,如图所示)

[Second:在 widget.cpp 中实现槽函数,并且关联信号和槽](#Second:在 widget.cpp 中实现槽函数,并且关联信号和槽)

Third:运行结果

3.2.4:小结

4:带参数的信号和槽

4.1:代码示例一

[First:在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数](#First:在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数)

[Second:在 "Widget.cpp" ⽂件实现重载槽函数以及连接信号和槽](#Second:在 "Widget.cpp" ⽂件实现重载槽函数以及连接信号和槽)

Third:执行结果

widget.h

widget.cpp

main.cpp

4.2:代码示例二(信号槽参数列表匹配规则)

[First:在 "widget.h" 头文件中声明信号和槽函数.](#First:在 "widget.h" 头文件中声明信号和槽函数.)

[Second:在 "widget.cpp" 文件中实现槽函数以及连接信号和槽](#Second:在 "widget.cpp" 文件中实现槽函数以及连接信号和槽)

Third:运行结果

widget.h

widget.cpp

main.cpp

5:信号与槽的连接方式

5.1:一对一连接

5.1.1:一个信号连接一个槽

widget.h

widget.cpp

main.cpp

5.1.2:信号与信号进行连接

5.2:一对多连接

widget.h

widget.cpp

main.cpp

5.3:多对一连接

widget.h

widget.cpp

main.cpp

6:信号与槽的断开

6.1:代码示例

widget.h

widget.cpp

main.cpp

断开信号前

断开信号后

7:使用Lambda表达式定义槽函数

7.1:Lambda表达式的语法格式

[7.2:局部变量引入方式[ ]](#7.2:局部变量引入方式[ ])

7.3:代码示例1

widget.h

widget.cpp

main.cpp

7.4:代码示例2

widget.h

widget.cpp

main.cpp

7.5:代码示例3

widget.h

widget.cpp

main.cpp

7.6:函数参数()

7.7:选项Opt

widget.h

widget.cpp

main.cpp

7.8:Lambda表达式的返回值类型->

7.9:Lambda表达式的函数体{}

8:总结

9:知识补充


1:信号与槽概述

  • 在Qt中,用户和控件的每次交互过程称为⼀个事件。例如 "用户点击按钮" 是⼀个事件,"用户关 闭窗口" 也是⼀个事件。每个事件都会发出⼀个信号,例如用户点击按钮会发出 "按钮被点击" 的信号,用户关闭窗口会发出 "窗口被关闭" 的信号。
  • Qt 中的所有控件都具有接收信号的能力,⼀个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作.例如,按钮所在的窗口接收到"按钮被点击"的信号以后,会做出"关闭自己"的响应动作;再比如输入框自己接收到"输入框被点击"的信号后,会做出"显示闪烁的光标,等待用户输入数据"的响应动作.在Qt中,对信号做出的响应动作就称之为槽.
  • 信号与槽式Qt特有的消息传输机制,它能够将相互独立的控件关联起来.比如,"按钮"和"窗口"本身是两个独立的控件,点击"按钮"并不会对"窗口"造成任何影响.通过信号和槽的机制,可以将"按钮"和"窗口"关联起来,实现"点击按钮会使窗口关闭"的效果.

1.1:信号的本质

信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时 Qt 对应的窗口类会发出某个信号,以此对用户的操作做出反应.因此,**信号的本质就是事件.**例如

  • 窗口刷新
  • 鼠标移动、鼠标按下、鼠标释放
  • 键盘输入

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

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

在 Qt 中信号的发出者是某个实例化的类对象。

1.2:槽的本质

槽就是对信号响应的函数 .槽就是一个函数,与一般的C++函数是一样的,可以定义在类的任何位置,可以具有任何参数,可以被重载,也可以直接被调用(但是不能有默认参数).槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行.

  1. 信号和槽机制底层是通过函数间的相互调用实现的.每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数.例如: "按钮被按下" 这个信号可以⽤ clicked() 函数表示,"窗⼝关闭" 这个槽可以⽤ close() 函数表⽰,假如使⽤信号和槽机制.
  2. 信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于
  • 信号函数⽤signals 关键字修饰,槽函数⽤ public slots、protected slots 或者 private slots 修饰。signalsslots是 Qt 在 C++ 的基础上扩展的关键字,专⻔⽤来指明信号函数和槽函数.
  • 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)
    信号函数的定义是Qt自动在编译程序之前生成的**.** 编写 Qt 应⽤程序的程序猿⽆需关注.
    这种⾃动⽣成代码的机制称为 元编程 (Meta Programming) . 这种操作在很多场景中都能⻅
    到.

2:信号与槽的使用

2.1:连接信号与槽

在Qt中,QObject类提供了一个静态成员函数connect,该函数专门用来关联指定的信号函数与槽函数.

QObject 是 Qt 内置的父类. Qt 中提供的很多类都是直接或者间接继承⾃ QObject.

connect()函数原型:

cpp 复制代码
connect (const QObject *sender, 
 const char * signal ,
 const QObject * receiver , 
 const char * method , 
 Qt::ConnectionType type = Qt::AutoConnection )
  • sender:信号的发送者
  • signal:发送的信号类型
  • receiver:信号的接收者
  • method:接收信号的槽函数即这个对象该怎么处理.

2.1.1:纯代码方式

2.1.1.1:widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QPushButton"

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);

    void handleClick();
    ~Widget();

private:
    Ui::Widget *ui;
    QPushButton * myButton;
};
#endif // WIDGET_H
2.1.1.2:widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    myButton = new QPushButton(this);
    myButton->setText("hello world");
    //第一个参数为该信号由哪个控件发出来的,第二个参数表示信号的类型,第三个参数表示信号的处理对象,第四个参数表示对象的处理方式
    connect(myButton,&QPushButton::clicked,this,&Widget::handleClick);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleClick()
{
    if(myButton->text() == QString("hello world"))
    {
        myButton->setText("Hello Qt");
    }
    else if(myButton->text() == QString("hello Qt"))
    {
        myButton->setText("hello World");
    }
}
2.1.1.3:main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

2.1.2:图形化方式

2.1.2.1:widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QPushButton"

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);

    void handleClick();
    ~Widget();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
2.1.2.2:widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleClick()
{
    if(ui->pushButton->text() == QString("Hello world"))
    {
        ui->pushButton->setText("Hello QT");
    }
    else
    {
        ui->pushButton->setText("Hello world");
    }
}
2.1.2.3:main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

2.2:查看内置信号与槽

有的uu可能会有以下两个问题

  1. 你怎么知道QPushButton有个clicked信号.
  2. 你怎么知道QWidget有一个close槽.

核心只有一个:多看官方文档.

我们通过观察QPushButton,发现里头并没有clicked信号,此时莫急,当没有找到时,我们就继续去父类去查找.因此我们去他的⽗类 QAbstractButton 中继续查找关键字signals.

  • 这⾥的clicked() 就是要找的信号。槽函数的寻找⽅式和信号⼀样,只不过它的关键字是slot .

2.3:通过Qt Creator生成槽代码

Qt Creator 可以快速帮助我们⽣成信号槽相关的代码

2.3.1:First

  • 新建项⽬,如下图为新建完成之后所包含的所有⽂件.

注意:创建时要生成ui文件.

2.3.2:Second

  • 双击 widget.ui ⽂件,进⼊ UI 设计界⾯.

2.3.3:Third

  • 在 UI 设计窗⼝中拖⼊⼀个 "按钮" ,并且修改 "按钮" 的名称及字体大小等;

2.3.4:Fourth

  • 可视化⽣成槽函数.


当单击 "转到槽..." 之后,出现如下界⾯:对于按钮来说,当点击时发送的信号是:clicked(),所以此处选择:clicked()

对于普通按钮来说, 使⽤ clicked 信号即可. clicked(bool) 没有意义的. 具有特殊状态的按
钮(比如复选按钮)才会⽤到 clicked(bool)

2.3.5:Fifth

  • ⾃动⽣成槽函数原型框架;

⾃动⽣成槽函数的名称有⼀定的规则。槽函数的命名规则为:on_XXX_SSS,其中:
1、以 " on " 开头,中间使⽤下划线连接起来;
2、" XXX " 表⽰的是对象名(控件的 objectName 属性)。
3、" SSS " 表⽰的是对应的信号。
如:" on_pushButton_clicked() ",pushButton 代表的是对象名,clicked 是对应的信号。

  • 在 "widget.cpp" 中⾃动⽣成槽函数定义.

2.3.6:Sixth

  • 在槽函数函数定义中添加要实现的功能. 实现关闭窗⼝的效果.

3:自定义信号与槽

3.1:自定义槽函数

3.1.1:语法

在 Qt 中,允许⾃定义信号的发送⽅以及接收⽅,即可以⾃定义信号函数和槽函数。但是对于⾃定义的信号函数和槽函数有⼀定的书写规范。

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

3.1.2:代码示例1

First:在widget.h声明自定义的槽函数

此处的slots是Qt中自己扩展的关键字(不是C++标准中的语法)

  • Qt中广泛包含了元编程技术(基于代码,生成代码).
  • qmake构建Qt项目的时候,就会调用专门的扫描器,扫描代码特定的关键字.
  • 基于关键字自动生成一大堆相关的代码.
Second:在widget.cpp中实现槽函数,并且关联信号与槽
Third:运行结果
widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
public slots:
   void handleClicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    QPushButton * Button = new QPushButton(this);
    Button->setText("按钮");
    Button->move(200,200);

    connect(Button,&QPushButton::clicked,this,&Widget::handleClicked);
}

void Widget::handleClicked()
{
    this->setWindowTitle("按钮已经被按下");
}

Widget::~Widget()
{
    delete ui;
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

3.1.3:代码示例2

First:点击widget.ui文件,并且拖拽控件
Second:右击输入框,并选择转到槽

当点击确定后,Qt Creator直接给我们生成好了一个函数.只需要在其内部编写代码即可

Third:编写自定义槽函数
Forth:运行结果
widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    this->setWindowTitle("Hello Qt");
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

3.1.4:小结

  • 如果我们通过图形化界面创建控件,还是推荐上面这种快速的方式来连接槽.
  • 如果是通过代码的方式来创建控件,那么就需要手动连接connect.

3.2:自定义信号

Qt中也允许自定义信号,但是其实比较少见,实际开发中很少会需要自定义信号,因为

  1. 信号就对应到用户的某个操作~
  2. 在GUI,用户能够进行哪些操作,是可以无限枚举的,Qt内置的信号,已经基本覆盖了用户所有可能的操作.
  3. 因此使用Qt内置的信号,就足以应付大部分的开发场景了.

3.2.1:基本语法

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

3.2.2:发送信号

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

3.2.3:代码示例

First:在 widget.h 中声明自定义的信号,如图所示
Second:在 widget.cpp 中实现槽函数,并且关联信号和槽
Third:运行结果

3.2.4:小结

  • 将信号与槽建立了连接,不代表信号发送出来了!
  • 那么如果才能发送自定义的信号呢,Qt内置的信号都不需要我们手动通过代码来触发
  • 用户在GU进行某些操作,就会自动触发对应的信号(发送信号的代码已经内置到Qt框架中了).

4:带参数的信号和槽

Qt的信号和槽也支持带有参数,同时可以支持重载.

  • 当信号带有参数的时候,槽的参数必须和信号的参数一致.
  • 参数要求一致,主要是要求参数的类型得一致,个数不一致也可以.
  • 但是当参数个数不一致的时候,要求信号的参数个数必须比槽函数的参数个数要多一些.

4.1:代码示例一

First:在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数

Second:在 "Widget.cpp" ⽂件实现重载槽函数以及连接信号和槽

Third:执行结果

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
signals:
    //信号函数声明
    void MySignal();
    //信号函数的重载
    void MySignal(const QString &);
public slots:
    //槽函数的声明
    void MySlot();
    //槽函数的重载
    void MySlot(const QString &);

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    void (Widget::*Psignal)(const QString &)= &Widget::MySignal;
    void (Widget::*Pslot)(const QString &)= &Widget::MySlot;
    //链接信号与槽
    connect(this,Psignal,this,Pslot);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::MySlot()
{
    qDebug()<<"调用Myslot()";
}

void Widget::MySlot(const QString &)
{
    qDebug()<<"调用Myslot(const QString &)";
}

void Widget::on_pushButton_clicked()
{
    emit MySignal("hello world");
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

4.2:代码示例二(信号槽参数列表匹配规则)

First:在 "widget.h" 头文件中声明信号和槽函数.

Second:在 "widget.cpp" 文件中实现槽函数以及连接信号和槽

Third:运行结果

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
signals:
    void MySignal(const QString & text);
public slots:
    void Myslots(const QString & text);
private slots:
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //链接信号与槽
    connect(this,&Widget::MySignal,this,&Widget::Myslots);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::Myslots(const QString &text)
{
    this->setWindowTitle(text);
}


void Widget::on_pushButton_clicked()
{
    emit MySignal("这是标题1");
}


void Widget::on_pushButton_2_clicked()
{
    emit MySignal("这是标题2");
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

那么我们思考下,应该是要求信号的参数跟槽函数的参数一致,那么这里为什么还会允许信号的参数比槽函数的参数个数多呢

  • 因为一个槽函数可能绑定了多个信号.
  • 如果严格要求一致,就意味着信号绑定到槽函数的要求变高了,换而言之,在当下这样的规则下,就允许信号与槽之间的绑定更加灵活了,更多的信号可以绑定在槽函数上了.
  • 个数不一致时,槽函数会按照参数顺序,拿到信号的前面N个参数,至少要确定,槽函数的每个参数都是有值的.

5:信号与槽的连接方式

5.1:一对一连接

主要是分别有两种形式,分别是

  1. 一个信号连接一个槽.
  2. 一个信号连接一个信号.

5.1.1:一个信号连接一个槽

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
signals:
    void Mysignal();
public:
    void HandleMysignal();
private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //链接信号与槽
    connect(this,&Widget::Mysignal,this,&Widget::HandleMysignal);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::HandleMysignal()
{
    qDebug()<<"接收到了信号";
}

void Widget::on_pushButton_clicked()
{
    //发送信号
    emit Mysignal();
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

5.1.2:信号与信号进行连接

信号与信号的链接,只需要在上面代码的基础上添加下面的代码即可~

5.2:一对多连接

一个信号连接多个槽

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void EmitSignal();
signals:
    void Mysignal();
public slots:
    void Myslots1();
    void Myslots2();
    void Myslots3();
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton * Btn = new QPushButton("按钮",this);
    //链接信号与槽
    connect(this,&Widget::Mysignal,this,&Widget::Myslots1);
    connect(this,&Widget::Mysignal,this,&Widget::Myslots2);
    connect(this,&Widget::Mysignal,this,&Widget::Myslots3);
    //进行链接
    connect(Btn,&QPushButton::clicked,this,&Widget::Mysignal);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::Myslots1()
{
    qDebug()<<"第一个信号";
}

void Widget::Myslots2()
{
    qDebug()<<"第二个信号";
}

void Widget::Myslots3()
{
   qDebug()<<"第三个信号";
}

void Widget::EmitSignal()
{
    emit Mysignal();
}

main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

5.3:多对一连接

  • 多个信号连接多个槽
widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
signals:
    void Mysignal1();
    void Mysignal2();
public:
    void Myslot();
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this,&Widget::Mysignal1,this,&Widget::Myslot);
    connect(this,&Widget::Mysignal2,this,&Widget::Myslot);
    emit Mysignal1();
    emit Mysignal2();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::Myslot()
{
    qDebug()<<"Myslot";
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

  • PS:同样可以进行多对多连接,这里博主就不演示啦,uu们下去后可以自己自行演示下~

6:信号与槽的断开

信号与槽函数之间是可以通过disconnect函数来断开信号与槽的连接.

PS:disconnect函数用的还是比较少的,大部分情况下,把信号与槽函数连接上了以后,就不必再管了,主动断开往往是把信号重新绑定到另一个槽函数上.

6.1:代码示例

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
public:
    void Myslots();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton * Btn = new QPushButton("按钮",this);
    Btn->move(200,200);
    connect(Btn,&QPushButton::clicked,this,&Widget::Myslots);
    disconnect(Btn,&QPushButton::clicked,this,&Widget::Myslots);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::Myslots()
{
    this->setWindowTitle("Hello world");
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

断开信号前

断开信号前,我们点击按钮,此时窗口的标题会显示hello world.

断开信号后

当断开信号后,此时我们无论如何点击按钮,窗口的标题都不会发生改变.

7:使用Lambda表达式定义槽函数

如果我们想方便地编写槽函数,比如在编写函数时连函数名都不想定义,则可以通过Lambda表达式来达到这个目的.

Lambda表达式的本质是一个"匿名函数",主要应用在"回调函数"的场景中.

7.1:Lambda表达式的语法格式

bash 复制代码
[capture] (params) opt->ret
{
    Function body;
};

|-------------------|-----------|
| capture | 捕获列表 |
| params | 参数表 |
| opt | 函数选项 |
| ret | 返回值类型 |
| Function body | 函数体 |

7.2:局部变量引入方式[ ]

  • [ ]:标识一个Lambda表达式的开始,不可忽略

|-------------|----------------------------------------|
| 符号 | 说明 |
| [] | 局部变量捕获列表.Lambda表达式不能访问外部函数体的任何局部变量 |
| [a] | 在函数体内部使用值传递的方式访问变量a |
| [&a] | 在函数体内部使用引用传递的方式访问变量a |
| [=] | 函数外的所有局部变量都通过值传递的方式使用,函数体内使用的是副本 |
| [&] | 以引用的方式使用Lambda表达式外部的所有变量. |
| [=,&foo] | foo使用引用方式,其余则是值传递的方式. |
| [&,foo] | foo使用值传递方式,其余使用引用传递 |
| [this] | 在函数内部可以使用类的成员函数与成员变量,=和&形式也都会默认引入. |

  • 由于使⽤引⽤⽅式捕获对象会有局部变量释放了而Lambda函数还没有被调⽤的情况。如果执行Lambda函数,那么引⽤传递⽅式捕获进来的局部变量的值不可预知。所以绝⼤多数场合使⽤的形式为: [=] () { }
    • 早期版本的 Qt,若要使⽤Lambda表达式,要在 ".pro" ⽂件中添加: CONFIG += C++11
      因为 Lambda表达式 是 C++11 标准提出的。Qt5 以上的版本⽆需⼿动添加,在新建项⽬时会⾃动添加。

7.3:代码示例1

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

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;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton * Btn = new QPushButton("按钮",this);
    [=]()
    {
        Btn->setText("测试Lambuda表达式");
    }();
}

Widget::~Widget()
{
    delete ui;
}
main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

7.4:代码示例2

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

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;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton * Btn1 = new QPushButton("按钮1",this);
    QPushButton * Btn2 = new QPushButton("按钮2",this);
    Btn2->move(200,200);
    [=]()
    {
        Btn1->setText("测试按钮1");
        Btn2->setText("测试按钮2");
    }();
}

Widget::~Widget()
{
    delete ui;
}

main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

7.5:代码示例3

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

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;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton * Btn1 = new QPushButton("按钮1",this);
    QPushButton * Btn2 = new QPushButton("按钮2",this);
    Btn2->move(200,200);
    [Btn1,Btn2]()
    {
        Btn1->setText("测试按钮1");
        Btn2->setText("测试按钮2");
    }();
}

Widget::~Widget()
{
    delete ui;
}

main.cpp
cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

7.6:函数参数()

(params) 表示Lambda函数对象接收的参数,类似于函数定义中的⼩括号表⽰函数接收的参数类型和个数。参数可以通过按值(如:(int a,int b))和按引⽤(如:(int &a,int &b))两种⽅式进⾏传递。函数参数部分可以省略,省略后相当于⽆参的函数。

7.7:选项Opt

Opt 部分是可选项,最常⽤的是 mutable声明 ,这部分可以省略
Lambda表达式外部的局部变量通过值传递进来时,其默认是 const ,所以不能修改这个局部变量的拷贝,加上mutable 就可以修改.

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

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;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton * Btn1 = new QPushButton("Btn1",this);
    QPushButton * Btn2 = new QPushButton("Btn2",this);

    int m = 10;
    Btn1->move(300,100);
    Btn2->move(500,100);

    connect(Btn1,&QPushButton::clicked,this,[m]()mutable{m = 20;qDebug()<< m;});
    connect(Btn2,&QPushButton::clicked,this,[=](){qDebug()<< m;});
}

Widget::~Widget()
{
    delete ui;
}

main.cpp

cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

7.8:Lambda表达式的返回值类型->

可以指定 Lambda表达式 返回值类型;如果不指定返回值类型,则编译器会根据代码实现为函数推导⼀个返回类型;如果没有返回值,则可忽略此部分.

7.9:Lambda表达式的函数体{}

Lambda表达式的函数体部分与普通函数体⼀致。⽤ { } 标识函数的实现,不能省略,但函数体可以为空.

8:总结

那么到这里,Qt中的信号与槽的核心内容博主就讲完啦,接下来我们来小结一下

1:首先学习了信号槽是啥.

  1. 信号源
  2. 信号的类型
  3. 信号的处理方式

2:然后再是信号槽的使用------>connect函数.

3:如何查阅文档

  1. 一个控件,内置了哪些信号,信号都是何时触发.
  2. 一个控件,内置了哪些槽,槽都是什么作用.
  3. 很可能需要的信号槽,需要到该类的父类/爷爷类/祖宗类去进行查询.

4:自定义槽函数

  1. 本质上就是自定义了一个普通的成员函数.
  2. 还可以让Qt Creator自动生成(虽然没有显式connect,但是可以通过函数名字特定规则来完成自动连接).

5:自定义信号

  1. 信号的本质是成员函数(函数的定义是Qt自己生成的,我们只需要写函数声明)
  2. signals:自定义关键字中
  3. emit来完成信号的发射(emit也可以省略)

6:带参数的信号与槽

  1. 发射信号的时候,把参数传递给对应的槽.
  2. 信号的参数和槽的参数要一致.
  3. 首先是类型要匹配.
  4. 其次是信号的参数个数要多于槽函数的参数个数.

7:信号与槽的存在意义

  1. 解耦合
  2. 连接方式

8:disconnect函数的使用方式.

9:Lambda表达式,简化槽函数的定义.

9:知识补充

这里博主给uu们详细讲解下什么是耦合与内聚

平常我们写代码时,要追求"高内聚,低耦合",这里博主通过一个故事来讲解

  • 小明,有一个女神,是他的高中同桌,他两的关系好到让小明以为他就是女神关系最好的异性朋友,然而到了大学,他们是在不同的省份上大学,但是小明依旧对女神穷追猛打,可最终换来的还是一张"好人卡",甚至小明发现他还是一个备胎,后面过了很多年,小明也找到了自己的爱妻,与他高中女神的关系也就是一个朋友圈的点赞之交.
  • 在去年有一次,小明的爱妻生病了,得了胆结石,发作比较疼,那么就要去医院做手术,把胆切掉,对小明来说,他就得把手头上的工作暂停,跟他上班的公司请假一段时间去专门照顾他的媳妇.
  • 那么很明显,小明的媳妇生病了,对他就会有十分明显的影响,这放在计算机里头就是高耦合,一个模块出了大问题会影响到另外一个模块.
  • 但是反过来,如果是小明的高中女神生病了,也要去医院做手术,对小明来说,顶多就是给他女神的朋友圈点个赞,然后小明该干嘛还是干嘛.
  • 那么很明显,女神生病,对小明来说,没有很大的影响,这放在计算机里头就是低耦合,一个模块出了问题对另一个模块影响甚微.
  • 小明的媳妇,有一个比较典型的缺点,那就是拿到什么东西就会随手一丢,衣服就会丢得导出都是,衣服可能在沙发上,可能在床上,可能在地上,可能在椅子上,也可能在沙发缝里头,总之就是不在衣柜里头,那么小明为了找到某一件衣服,小明就需要把家里的每一个角落都遍历一遍.而这就是低内聚
  • 低内聚**:写代码的时候,实现某个功能点,围绕这个功能的相关被放到整个项目的各个地方.**
  • 但是如果小明的媳妇十分有纪律,衣服都是放在固定的地方,要么衣柜里头,要么在脏衣篓里头,此时要找到某个衣服就十分好找了,这就是高内聚.
  • 高内聚**:写代码的时候,某个功能点的实现都被集中放到了一起.**
相关推荐
开始了码2 小时前
QT::键盘事件简单介绍
qt
lijiatu100862 小时前
[C++ ]qt槽函数及其线程机制
c++·qt
i***17182 小时前
使用 Qt 插件和 SQLCipher 实现 SQLite 数据库加密与解密
数据库·qt·sqlite
遇到困难睡大觉哈哈2 小时前
HarmonyOS IPC/RPC 实战:用 ArkTS 跑通 Proxy–Stub 整条链路
qt·rpc·harmonyos·鸿蒙
寻找华年的锦瑟11 小时前
Qt-视频九宫格布局
开发语言·qt
雨田哥11 小时前
Qt AFSim雷达探测显示
qt·afsim·qt雷达·qt仿真·雷达显控端·qt雷达模拟器
努力的ping12 小时前
qt信号和槽
开发语言·qt
AGANCUDA12 小时前
qt中vtk显示pcl的点云类
开发语言·qt
xxp432114 小时前
Qt 网络编程 TCP通信
开发语言·qt