[2.3:通过Qt Creator生成槽代码](#2.3:通过Qt Creator生成槽代码)
Second:在widget.cpp中实现槽函数,并且关联信号与槽
[First:在 widget.h 中声明自定义的信号,如图所示](#First:在 widget.h 中声明自定义的信号,如图所示)
[Second:在 widget.cpp 中实现槽函数,并且关联信号和槽](#Second:在 widget.cpp 中实现槽函数,并且关联信号和槽)
[First:在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数](#First:在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数)
[Second:在 "Widget.cpp" ⽂件实现重载槽函数以及连接信号和槽](#Second:在 "Widget.cpp" ⽂件实现重载槽函数以及连接信号和槽)
[First:在 "widget.h" 头文件中声明信号和槽函数.](#First:在 "widget.h" 头文件中声明信号和槽函数.)
[Second:在 "widget.cpp" 文件中实现槽函数以及连接信号和槽](#Second:在 "widget.cpp" 文件中实现槽函数以及连接信号和槽)
[7.2:局部变量引入方式[ ]](#7.2:局部变量引入方式[ ])
1:信号与槽概述
- 在Qt中,用户和控件的每次交互过程称为⼀个事件。例如 "用户点击按钮" 是⼀个事件,"用户关 闭窗口" 也是⼀个事件。每个事件都会发出⼀个信号,例如用户点击按钮会发出 "按钮被点击" 的信号,用户关闭窗口会发出 "窗口被关闭" 的信号。
- Qt 中的所有控件都具有接收信号的能力,⼀个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作.例如,按钮所在的窗口接收到"按钮被点击"的信号以后,会做出"关闭自己"的响应动作;再比如输入框自己接收到"输入框被点击"的信号后,会做出"显示闪烁的光标,等待用户输入数据"的响应动作.在Qt中,对信号做出的响应动作就称之为槽.
- 信号与槽式Qt特有的消息传输机制,它能够将相互独立的控件关联起来.比如,"按钮"和"窗口"本身是两个独立的控件,点击"按钮"并不会对"窗口"造成任何影响.通过信号和槽的机制,可以将"按钮"和"窗口"关联起来,实现"点击按钮会使窗口关闭"的效果.

1.1:信号的本质
信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时 Qt 对应的窗口类会发出某个信号,以此对用户的操作做出反应.因此,**信号的本质就是事件.**例如
- 窗口刷新
- 鼠标移动、鼠标按下、鼠标释放
- 键盘输入
那么在 Qt 中信号是通过什么形式呈现给使⽤者的呢?
- 我们对哪个窗⼝进⾏操作, 哪个窗⼝就可以捕捉到这些被触发的事件。
- 对于使⽤者来说触发了⼀个事件我们就可以得到 Qt 框架给我们发出的某个特定信号。
- 信号的呈现形式就是函数, 也就是说某个事件产⽣了, Qt 框架就会调⽤某个对应的信号函数, 通知使⽤者。
在 Qt 中信号的发出者是某个实例化的类对象。
1.2:槽的本质
槽就是对信号响应的函数 .槽就是一个函数,与一般的C++函数是一样的,可以定义在类的任何位置,可以具有任何参数,可以被重载,也可以直接被调用(但是不能有默认参数).槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行.
- 信号和槽机制底层是通过函数间的相互调用实现的.每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数.例如: "按钮被按下" 这个信号可以⽤ clicked() 函数表示,"窗⼝关闭" 这个槽可以⽤ close() 函数表⽰,假如使⽤信号和槽机制.
- 信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于
- 信号函数⽤signals 关键字修饰,槽函数⽤ public slots、protected slots 或者 private slots 修饰。signals 和 slots是 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可能会有以下两个问题
- 你怎么知道QPushButton有个clicked信号.
- 你怎么知道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 中,允许⾃定义信号的发送⽅以及接收⽅,即可以⾃定义信号函数和槽函数。但是对于⾃定义的信号函数和槽函数有⼀定的书写规范。
- 早期的 Qt 版本要求槽函数必须写到 "public/private/protected slots" 下,但是现在⾼级版本的 Qt 允许写到类的 "public" 作⽤域中或者全局下;
- 返回值为 void,需要声明,也需要实现;
- 可以有参数,可以发生重载.
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中也允许自定义信号,但是其实比较少见,实际开发中很少会需要自定义信号,因为
- 信号就对应到用户的某个操作~
- 在GUI,用户能够进行哪些操作,是可以无限枚举的,Qt内置的信号,已经基本覆盖了用户所有可能的操作.
- 因此使用Qt内置的信号,就足以应付大部分的开发场景了.
3.2.1:基本语法
- ⾃定义信号函数必须写到 "signals" 下.
- 返回值为 void,只需要声明,不需要实现.
- 可以有参数,也可以发⽣重载.
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:一对一连接
主要是分别有两种形式,分别是
- 一个信号连接一个槽.
- 一个信号连接一个信号.
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 以上的版本⽆需⼿动添加,在新建项⽬时会⾃动添加。
- 早期版本的 Qt,若要使⽤Lambda表达式,要在 ".pro" ⽂件中添加: CONFIG += C++11

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:首先学习了信号槽是啥.
- 信号源
- 信号的类型
- 信号的处理方式
2:然后再是信号槽的使用------>connect函数.
3:如何查阅文档
- 一个控件,内置了哪些信号,信号都是何时触发.
- 一个控件,内置了哪些槽,槽都是什么作用.
- 很可能需要的信号槽,需要到该类的父类/爷爷类/祖宗类去进行查询.
4:自定义槽函数
- 本质上就是自定义了一个普通的成员函数.
- 还可以让Qt Creator自动生成(虽然没有显式connect,但是可以通过函数名字特定规则来完成自动连接).
5:自定义信号
- 信号的本质是成员函数(函数的定义是Qt自己生成的,我们只需要写函数声明)
- signals:自定义关键字中
- emit来完成信号的发射(emit也可以省略)
6:带参数的信号与槽
- 发射信号的时候,把参数传递给对应的槽.
- 信号的参数和槽的参数要一致.
- 首先是类型要匹配.
- 其次是信号的参数个数要多于槽函数的参数个数.
7:信号与槽的存在意义
- 解耦合
- 连接方式
8:disconnect函数的使用方式.
9:Lambda表达式,简化槽函数的定义.
9:知识补充
这里博主给uu们详细讲解下什么是耦合与内聚
平常我们写代码时,要追求"高内聚,低耦合",这里博主通过一个故事来讲解
- 小明,有一个女神,是他的高中同桌,他两的关系好到让小明以为他就是女神关系最好的异性朋友,然而到了大学,他们是在不同的省份上大学,但是小明依旧对女神穷追猛打,可最终换来的还是一张"好人卡",甚至小明发现他还是一个备胎,后面过了很多年,小明也找到了自己的爱妻,与他高中女神的关系也就是一个朋友圈的点赞之交.
- 在去年有一次,小明的爱妻生病了,得了胆结石,发作比较疼,那么就要去医院做手术,把胆切掉,对小明来说,他就得把手头上的工作暂停,跟他上班的公司请假一段时间去专门照顾他的媳妇.
- 那么很明显,小明的媳妇生病了,对他就会有十分明显的影响,这放在计算机里头就是高耦合,一个模块出了大问题会影响到另外一个模块.
- 但是反过来,如果是小明的高中女神生病了,也要去医院做手术,对小明来说,顶多就是给他女神的朋友圈点个赞,然后小明该干嘛还是干嘛.
- 那么很明显,女神生病,对小明来说,没有很大的影响,这放在计算机里头就是低耦合,一个模块出了问题对另一个模块影响甚微.
- 小明的媳妇,有一个比较典型的缺点,那就是拿到什么东西就会随手一丢,衣服就会丢得导出都是,衣服可能在沙发上,可能在床上,可能在地上,可能在椅子上,也可能在沙发缝里头,总之就是不在衣柜里头,那么小明为了找到某一件衣服,小明就需要把家里的每一个角落都遍历一遍.而这就是低内聚
- 低内聚**:写代码的时候,实现某个功能点,围绕这个功能的相关被放到整个项目的各个地方.**
- 但是如果小明的媳妇十分有纪律,衣服都是放在固定的地方,要么衣柜里头,要么在脏衣篓里头,此时要找到某个衣服就十分好找了,这就是高内聚.
- 高内聚**:写代码的时候,某个功能点的实现都被集中放到了一起.**





