HOME
BLOG
Qt信号槽
Jun 03 2023

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类中自定义信号槽, 需要满足一些条件, 并且有些事项也需要注意:

  1. 要编写新的类并且让其继承Qt的某些标准类
  2. 这个新的子类必须从QObject类或者是QObject子类进行派生
  3. 在定义类的头文件中加入 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);
Qt学习