QT信号槽
所谓信号槽,实际就是观察者模式 (发布 - 订阅模式)。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发
在 Qt 中我们需要使用 QOjbect类中的 connect去连接信号和信号槽函数
QMetaObject::Connection QObject::connect(
const QObject *sender, PointerToMemberFunction signal,
const QObject *receiver, PointerToMemberFunction method,
Qt::ConnectionType type = Qt::AutoConnection);
参数:
- sender: 发出信号的对象
- signal: 属于sender对象, 信号是一个函数, 这个参数的类型是函数
指针, 信号函数地址
- receiver: 信号接收者
- method: 属于receiver对象, 当检测到sender发出了signal信号,
receiver对象调用method方法,信号发出之后的处理动作
// 参数 signal 和 method 都是函数地址, 因此简化之后的 connect() 如下:
connect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::method);
使用connect()进行信号槽连接的注意事项:
connect函数相对于做了信号处理动作的注册
调用conenct函数的sender对象的信号并没有产生, 因此receiver对象的 method 也不会被调用
method槽函数本质是一个回调函数, 调用的时机是信号产生之后, 调用是Qt框架来执行的
connect中的sender和recever两个指针必须被实例化了, 否则conenct不会成功
按钮就是信号的发送者,窗口接收发来的信号去处理
标准槽函数
示例:
处理过程:程序运行,点击按钮,关闭窗口
在mainwindow窗口里面做如下操作,对应ui界面的closebt这个按钮,clicked点击发信号,发给this当前窗口对象,当前窗口接收到,做出close这个操作
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->closebt,&QPushButton::clicked,this,&MainWindow::close);
}
当然了标准的槽函数,就是那个处理函数肯定不能满足我们日常需求,所以我们需要自定义槽函数(回调)
自定义信号槽
如果想要在QT类中自定义信号槽, 需要满足一些条件, 并且有些事项也需要注意:
- 要编写新的类并且让其继承Qt的某些标准类
- 这个新的子类必须从QObject类或者是QObject子类进行派生
- 在定义类的头文件中加入 Q_OBJECT 宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:
class MyMainWindow : public QWidget
{
Q_OBJECT
......
}
demo:
构建一个son和father类
father.h
#ifndef FATHER_H
#define FATHER_H
#include <QObject>
class father : public QObject
{
Q_OBJECT
public:
explicit father(QObject *parent = nullptr);
public:
//槽函数
void eatrice();
};
#endif // FATHER_H
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"son.h"
#include"father.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void eating();//槽函数
private:
Son *s;
father* f;
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
son.h
#ifndef SON_H
#define SON_H
#include <QObject>
class Son : public QObject
{
Q_OBJECT
public:
explicit Son(QObject *parent = nullptr);
signals:
//信号函数
void eat();
};
#endif // SON_H
加了signal关键字,就不用再去定义了,只需要声明
father.cpp
做出信号槽函数定义
#include "father.h"
#include<QDebug>
father::father(QObject *parent) : QObject(parent)
{
}
void father::eatrice()
{
qDebug()<<"来吃大米饭.";
}
son.cpp什么也不做,因为signal带定义,不需要我们自己去cpp文件重写具体实现
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
s=new Son;
f=new father;
connect(s,&Son::eat,f,&father::eatrice);
connect(ui->eating,&QPushButton::clicked,this,&MainWindow::eating);
connect(ui->closebt,&QPushButton::clicked,this,&MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::eating()
{
//发射自定义信号
s->eat();
}
运行程序:
每点击一次eat按钮,回调就做一次反应
一个信号可以对接多个槽函数
一个槽函数也能接收多个信号
在mainwindow里面实现一个sleep函数,加个connect,也去相应eat按钮
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
s=new Son;
f=new father;
connect(s,&Son::eat,f,&father::eatrice);
connect(s,&Son::eat,this,&MainWindow::sleep);
connect(ui->eating,&QPushButton::clicked,this,&MainWindow::eating);
connect(ui->closebt,&QPushButton::clicked,this,&MainWindow::close);
}
void MainWindow::sleep()
{
qDebug()<<"快去睡觉.";
}
点击窗口,新的槽函数也去相应处理
信号也能连接信号,相当于传递了数据
对上面做如下改动:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
s=new Son;
f=new father;
connect(s,&Son::eat,f,&father::eatrice);
connect(s,&Son::eat,this,&MainWindow::sleep);
//这样直接用信号,相当于下面我们自己实现的发信号的函数就可以不用了
connect(ui->eating,&QPushButton::clicked,s,&Son::eat);
//connect(ui->eating,&QPushButton::clicked,this,&MainWindow::eating);
connect(ui->closebt,&QPushButton::clicked,this,&MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
/*
void MainWindow::eating()
{
//发射自定义信号
s->eat();
}*/
void MainWindow::sleep()
{
qDebug()<<"快去睡觉.";
}
程序运行结果和上面一样
信号槽是可以断开的
disconnect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::method);