一、基本使用
- 信号(Signal):比如点击按钮就会发出一个点击信号
- 槽(Slot):一般也叫槽函数,是用来处理信号的函数
- 官方文档参考:Signals & Slots
- 上图中的效果是:
- Object1发出信号signal1,交给Object2的槽slot1、slot2去处理
- Object1是信号的发送者,Object2是信号的接收者
- Object1发出信号signal2,交给Object4的槽slot1去处理
- Object1是信号的发送者,Object4是信号的接收者
- Object3发出信号signal1,交给Object4的槽slot3去处理
- Object3是信号的发送者,Object4是信号的接收者
- 1个信号可以由多个槽进行处理,1个槽可以处理多个信号
- Object1发出信号signal1,交给Object2的槽slot1、slot2去处理
- 通过
connect
函数可以将信号的发送者
、信号
、信号的接收者
、槽
连接在一起。
connect(信号的发送者, 信号, 信号的接收者, 槽);
1、使用案例
- 在主窗口添加一个
关闭
按钮
#include "mainwindow.h"
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QPushButton *btn = new QPushButton;
btn->setText("关闭");
btn->setFixedSize(100, 50);
btn->setParent(this);
this->setFixedSize(500, 500);
}
- 运行后如下图
- 使用
command + 鼠标左键
点击QPushButton
, 可以查看QPushButton
的头文件
- 在
QPushButton
的头文件中, 并没有找到信号
的定义 - 继续查看父类
QAbstractButton
的头文件
- 在
QAbstractButton
的头文件中, 往下拉, 可以看到定义了四个信号(Q_SIGNALS)
- 使用同样的办法, 可以在
QMainWindow
的父类QWidget
中找到槽函数(Q_SLOTS)
- 通过
connect
函数, 链接QPushButton
和信号
与QMainWindow
的槽函数
#include "mainwindow.h"
#include <QDebug>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QPushButton *btn = new QPushButton;
btn->setText("关闭");
btn->setFixedSize(100, 50);
btn->setParent(this);
this->setFixedSize(500, 500);
connect(btn, &QPushButton::clicked, this, &MainWindow::close);
}
MainWindow::~MainWindow()
{
qDebug("主窗口被关闭了");
}
- 再次运行程序
- 点击关闭按钮, 可以看到
主窗口
被关闭, 同时有打印输出
- 可以通过
disconnect
函数断开信号与槽
的链接
// 可以通过disconnect断开连接
disconnect(btn, &QPushButton::clicked, this, &MainWindow::close);
二、自定义信号与槽
- 信号的发送者和接收者都必须继承自
QObject
,Qt中的控件最终都是继承自QObject,比如QMainWindow、QPushButton等。
1、信号的发送者
- 新建文件
sender
, 定义Sender
类作为信号的发送者
- 查看生成的文件
- sender.h
- Q_OBJECT用以支持自定义信号和槽
- 自定义的信号需要写在
signals:
下面 - 自定义的信号只需要声明,不需要实现
#ifndef SENDER_H
#define SENDER_H
#include <QObject>
class Sender : public QObject
{
Q_OBJECT
public:
explicit Sender(QObject *parent = nullptr);
signals:
// 定义信号, 只需要添加声明, 不需要添加实现
void exit();
};
#endif // SENDER_H
- sender.cpp
#include "sender.h"
Sender::Sender(QObject *parent) : QObject(parent)
{
}
2、信号的接收者
- 创建
receiver
, 定义Receiver
类, 作为信号的接收者
, 与sender
文件创建方法一致
- receiver.h
- 自定义的槽建议写在
public slots:
下面
- 自定义的槽建议写在
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
class Receiver : public QObject
{
Q_OBJECT
public:
explicit Receiver(QObject *parent = nullptr);
// 自定义槽
public slots:
void handleExit();
};
#endif // RECEIVER_H
- receiver.cpp
- 自定义的槽函数, 必须写出实现
#include "receiver.h"
#include <QDebug>
Receiver::Receiver(QObject *parent) : QObject(parent)
{
}
// 实现槽函数,编写处理信号的代码
void Receiver::handleExit()
{
qDebug() << "Receiver::handleExit()";
}
3、连接
- 使用
connect
函数连接sender
和receiver
- mainwindow.cpp
#include "mainwindow.h"
#include "sender.h"
#include "receiver.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
Sender *sender = new Sender;
Receiver *receiver = new Receiver;
connect(sender, &Sender::exit, receiver, &Receiver::handleExit);
// sender发送信号
emit sender->exit();
}
MainWindow::~MainWindow()
{
}
- 运行, 查看打印
- 如果不在使用sender和receiver, 记得要销毁对象
#include "mainwindow.h"
#include "sender.h"
#include "receiver.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
Sender *sender = new Sender;
Receiver *receiver = new Receiver;
connect(sender, &Sender::exit, receiver, &Receiver::handleExit);
// sender发送信号
emit sender->exit();
// 如果不在使用sender和receiver, 记得要销毁对象
delete sender;
delete receiver;
}
4、参数和返回值
- 信号与槽都可以有参数和返回值:
- 发信号时的参数会传递给槽
- 槽的返回值会返回到发信号的位置
// 自定义信号
signals:
int exit(int a, int b);
// 自定义槽
public slots:
int handleExit(int a, int b);
int Receiver::handleExit(int a, int b)
{
// Receiver::handleExit() 10 20
qDebug() << "Receiver::handleExit()" << a << b;
return a + b;
}
// 发出信号
int a = emit sender->exit(10, 20);
// 30
qDebug() << a;
- 需要注意的是:信号的参数个数必须大于等于槽的参数个数。比如下面的写法是错误的:
// 自定义信号
signals:
void exit(int a);
// 自定义槽
public slots:
void handleExit(int a, int b);
- 连接2个信号
- 比如下图,连接了Object 1的Signal 1A和Object 2的Signal 2A
- 当Object 1发出Signal 1A时,会触发Slot X、Slot Y
- 当Object 2发出Signal 2A时,只会触发Slot Y,而不会触发Slot X
- 创建
sender2
文件, 定义Sender2
类, 作为第二个信号的发送者
- 添加信号
#ifndef SENDER2_H
#define SENDER2_H
#include <QObject>
class Sender2 : public QObject
{
Q_OBJECT
public:
explicit Sender2(QObject *parent = nullptr);
signals:
// 定义信号, 只需要添加声明, 不需要添加实现
int exit2(int a, int b);
};
#endif // SENDER2_H
receiver.h
中添加新的槽函数
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
class Receiver : public QObject
{
Q_OBJECT
public:
explicit Receiver(QObject *parent = nullptr);
// 自定义槽
public slots:
int handleExit(int num1, int num2);
int handleExit2(int num1, int num2);
};
#endif // RECEIVER_H
receiver.cpp
中添加槽函数的实现
#include "receiver.h"
#include <QDebug>
Receiver::Receiver(QObject *parent) : QObject(parent)
{
}
// 实现槽函数,编写处理信号的代码
int Receiver::handleExit(int num1, int num2)
{
qDebug() << "Receiver::handleExit()" << num1 << num2;
return num1 + num2;
}
int Receiver::handleExit2(int num1, int num2)
{
qDebug() << "Receiver::handleExit2()" << num1 << num2;
return num1 - num2;
}
- 在
mainwindow.cpp
中, 连接sender
和sender2
的信号
#include "mainwindow.h"
#include "sender.h"
#include "sender2.h"
#include "receiver.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
Sender *sender = new Sender;
Sender2 *sender2 = new Sender2;
Receiver *receiver = new Receiver;
// 连接 sender 和 sender2
connect(sender, &Sender::exit, sender2, &Sender2::exit2);
// 连接 sender 和 receiver
connect(sender, &Sender::exit, receiver, &Receiver::handleExit);
// 连接 sender2 和 receiver
connect(sender2, &Sender2::exit2, receiver, &Receiver::handleExit2);
// sender发送信号
int res = emit sender->exit(100, 200);
qDebug() << res;
// 如果不在使用sender、sender2和receiver, 记得要销毁对象
delete sender;
delete sender2;
delete receiver;
}
sender
发送信号
5、Lambda
Lambda
是一个函数代码块, 可以用于事件处理
connect(sender, &Sender::exit, [](int a, int b) {
qDebug() << "lambda handle exit";
return a + b;
});
#include "mainwindow.h"
#include "sender.h"
#include "sender2.h"
#include "receiver.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
Sender *sender = new Sender;
connect(sender, &Sender::exit, [](int a, int b) {
qDebug() << "lambda handle exit";
return a + b;
});
int res = emit sender->exit(10, 20);
qDebug() << res;
}
三、UI中的信号与槽
1、UI绑定槽
- 新建项目, 勾选UI
- 项目中生成
mainwindow.ui
文件
- 双击
mainwindow.ui
文件
- 添加两个按钮, 分别标为
登录
和注册
鼠标右键
点击登录按钮
- 选择
转到槽
- 选择
clicked
, 可以在mainwindow.h
和mainwindow.cpp
文件中自动生成槽函数
- 在
槽函数
中添加打印代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
qDebug() << "登录";
}
- 执行程序, 点击
登录
按钮, 可以看到控制台有打印输出
2、UI绑定槽的命名规则
- UI绑定槽的命名规则:
on_控件的变量名_事件名
- 上面的
登录
按钮, 在UI文件中名称是pushButton
, 选择的事件是clicked
- 修改
注册
按钮在UI中的名称为registerButton
- 在
mainwindow.h
文件中, 手动添加registerButton
触发的槽函数声明
- 在
mainwindow.cpp
文件中, 手动添加registerButton
触发的槽函数实现
- 执行程序, 点击
注册
按钮, 可以看到控制台的打印输出
于是得知:ui文件中的控件会自动跟
符合命名规则
的槽函数
建立连接。
3、通过代码获取UI文件中的控件
- ui文件中的控件可以在代码中通过
ui->变量名
访问。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 通过ui->访问ui文件中的2个按钮
ui->pushButton->setFixedSize(200, 50);
ui->registerButton->setFixedSize(200, 50);
}