一、概念
Qt中的信号有以下三个要素:
- 信号源:由哪个控件发出的信号
- 信号的类型:用户进行不同的操作,就可能触发不同的信号
- 信号的处理方式:槽(slot)->函数
Qt中可以使用connect这样的函数,把一个信号和一个槽关联起来,后续只要信号触发,Qt就会自动的执行槽函数(槽函数其实是一种回调函数)
注意:Qt中一定是先关联信号和槽,然后再触发这个信号,顺序
二、connect
connect函数是QObject提供的静态的成员函数
- sender:信号是哪个控件发出的
- signal:信号的类型
- receiver:哪个控件进行处理
- method:处理的函数
- 最后一个参数暂时不考虑,很少使用
示例:点击按钮就会关闭窗口
代码:
#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::close);
}
Widget::~Widget()
{
delete ui;
}
当点击窗口的关闭按钮,窗口就会关闭
三、自定义的槽函数
我们自己实现一个槽函数,然后调用它。与自定义一个成员函数是一样的。别忘记声明该函数
代码:
#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::handleClick);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClick()
{
// 按下按钮,修改窗口标题
this->setWindowTitle("按钮已经按下");
}
第二种调用信号槽的方式:
进入界面设计,把QPushButton按钮拉到窗口,右击选择转到槽,然后选择clicked,点击确定,就会在代码中显示这个函数:
void Widget::on_pushButton_clicked()
{
}
然后接下来我们实现这个函数:
void Widget::on_pushButton_clicked()
{
this->setWindowTitle("按钮已经按下");
}
运行,最终的效果与前面一样
这种方式不需要手动connect,因为Qt内部已经帮我们处理好了
四、自定义的信号
Qt中也是允许自定义信号的,在开发中,大部分情况是要自定义槽函数,来进行业务逻辑。相比之下,自定义信号更少见,因为实际开发中用的比较少。
信号是对应用户的某个操作,在GUI中,用户能够进行哪些操作,是可以穷举的,Qt内置的信号,基本上已经覆盖到了上述所有可能的用户操作。因此,使用Qt内置的信号,就足够了。
Widget是没有定义任何信号的,但是它继承了QWidget和QObject,这两个类已经提供了一些信号,可以直接使用。
信号,其实是一种特殊的函数。程序员只要写出函数声明,并且告诉Qt,这是一个信号就行了。这个函数的定义,是Qt在编译过程中自动生成的。
信号是Qt中特殊的机制,Qt生成的信号函数的实现,要配合Qt框架做很多既定的操作
作为信号函数,这个函数的返回值必须是void,有没有参数都行,也可以支持重载。
代码:
#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 handleMySignal();
signals:
void mySignal();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
#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::handleMySignal);
// 发送自定义信号
emit mySignal();
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleMySignal()
{
this->setWindowTitle("处理自定义信号");
}
注意:建立连接,不代表信号发出。Qt内置的信号都不需要我们手动来触发,自定义的信号需要关键字emit
运行:标题已改变
还可以通过图形界面化的方式:
然后创建槽函数:
#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::handleMySignal);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleMySignal()
{
this->setWindowTitle("处理自定义信号");
}
void Widget::on_pushButton_clicked()
{
// 发送自定义信号可以在任何合适的代码中
// 发送自定义信号
emit mySignal();
}
这段代码是编译器自动生成的
运行:点击按钮后标题修改
注意:在用图形化界面的按钮时,即使没有emit关键字,信号也能发送出去。但还是建议写上
五、带参数的信号和槽
信号和槽也可以带上参数,但是信号带参数时,槽函数的参数必须与信号的参数保持一致。此时发送信号,就可以给信号函数传递实参,然后这个参数就会被传递到槽函数中,最后达到让信号给槽传参的效果。
信号参数一致主要是类型一致,个数可以不一致,如果不一致,信号的参数个数必须比槽的参数个数多
信号处理函数的参数的具体内容是发送信号时传参给的
运行:点击按钮,标题修改
可以再搭配其他的参数,比如多设置一个按钮,然后在按钮点击事件函数里面提供不同的参数,达到同样的效果
六、信号和槽存在的意义
信号和槽解决的问题:响应用户的操作
优点:
- 解耦合:把触发用户操作的控件和处理对应用户的操作逻辑解耦合
- 多对多:一个信号可以connect多个槽函数,一个槽函数可以被多个信号connect
connect的作用:与关联表相同
所以:Qt引入信号槽机制,目的是为了能够让信号和槽之间按照多对多的方式来进行关联。但是实际开发中很少使用,大部分情况一对一就够了。
七、信号和槽的断开
使用disconnect来断开信号槽的连接,用法与connect相似
大部分情况信号和槽连接上就不用管了,主动断开是把信号重新绑定到另一个槽函数上。
先断开原来的连接,再连接新的槽函数:
#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::handleClick1);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClick1()
{
this->setWindowTitle("修改标题1");
}
void Widget::handleClick2()
{
this->setWindowTitle("修改标题2");
}
void Widget::on_pushButton_2_clicked()
{
disconnect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick1);
connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick2);
}
点击按钮修改标题,窗口的标题变成:
点击切换槽函数,再点击修改标题:
如果没有断开,两个槽函数都会执行
八、使用lambda表达式定义槽函数
声明和定义一步到位,不需要分开写。要注意lambda的语法形式
#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* button = new QPushButton(this);
button->move(200, 200);
button->setText("按钮");
connect(button, &QPushButton::clicked, this, [](){
qDebug() << "lambda执行";
});
}
Widget::~Widget()
{
delete ui;
}