(二)Qt的信号和槽机制

介绍

信号和槽之间的关系就是一个事件发生触发另一个事件的发生。使用这种机制的最好例子就是按钮的点击事件。但按钮发生了点击,可能触发“取消""确认""关闭"等等。
信号:就是相当于”起因",发送出去后,类似于广播,所以我们之后需要定义一个东西将“因"与"果"进行连接在一起;
槽函数:就是相当于”果",当之前绑定信号发出时,被连接的槽函数会自动被回调,做出相应的反应

系统自带的信号和槽函数的例子(按钮点击)

  • 定义好按钮
  QPushButton * quitBtn = new QPushButton("关闭窗口",this);
  • 确定好系统对于按钮的信号
    在帮助文档查找信号在这里插入图片描述

到其父类中查找信号,其中包含了我们需要的点击的信号在这里插入图片描述

  • 确定好系统对应界面的槽函数
    在这里插入图片描述

  • 建立信号和槽函数之间的绑定关系
    绑定信号和槽函数之间关系使用connect函数来实现
    其中connect函数包括四个参数:
    sender:发出信号的对象(发送者)
    signal:发出的具体信号(信号)
    receiver:接收信号的对象(接收者)
    slot:接收对象在接收到信号之后所需要调用的函数(槽函数)

connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);
  • 运行测试
    在这里插入图片描述

自定义信号和槽

需求介绍

下课后,老师饿了,学生请老师吃饭。
所以之间的信号和槽函数的绑定是:connect(老师,饿了,学生,请老师吃饭)

无参信号与槽实现流程

  • 分别定义老师类和学生类
    定义老师类
    创建好老师和学生类
  • 声明和实现分别的信号和槽
    首先在老师类中声明信号
    定义声明信号的注意事项:
    1.信号的返回值是void类型
    2.信号只需要声明不需要实现
    3.编写在信号一栏
#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>

class Teacher : public QObject
{
    Q_OBJECT
public:
    explicit Teacher(QObject *parent = nullptr);

signals://编写再此///////////////////////////////////////////////////////////////
    void hungry();

public slots:
};

#endif // TEACHER_H

接着在学生类中声明槽函数并在源文件实现
定义槽函数的注意事项:
1.槽函数的返回值是void类型
2.信号需要声明也需要实现
3.编写在类的槽函数一栏

//在学生类的头文件中声明槽函数
void treat();

可以快速在源文件中定义实现函数
在这里插入图片描述

//在学生类的源文件中实现槽函数
void Student::treat()
{
    qDebug()<<"请老师吃饭";
}
  • 在widget.h中引入刚刚定义的两个类,分别声明两个实例和定义一个下课的方法
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <teacher.h>
#include <student.h>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void classIsOver();//声明下课函数
private:
    Ui::Widget *ui;
    Teacher * te;//定义实例对象
    Student * st;//定义实例对象
};

#endif // WIDGET_H

  • 在widget.cpp中分别实例化两个实例和实现一个下课的方法,最重要的便是通过connect函数,实现信号和槽函数的绑定
    connect(老师,饿了,学生,请吃饭)
#include "widget.h"
#include "ui_widget.h"
#include <teacher.h>
#include <student.h>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->te = new Teacher(this);//实例对象
    this->st = new Student(this);//实例对象
    connect(te,&Teacher::hungry,st,&Student::treat);//绑定connect
    classIsOver();
}
void Widget::classIsOver(){
    emit this->te->hungry();//使用emit发送出老师饿了的信号
}
Widget::~Widget()
{
    delete ui;
}

  • 运行测试
    在这里插入图片描述

带参信号与槽实现重载

  • 教师类添加新的信号
 void hungry(QString foodname);
  • 学生类添加新的槽函数
void treat(QString foodname);
void Student::treat(QString foodname)
{
    qDebug()<<"请老师吃饭,老师想吃"<<foodname.toUtf8().data();//将qstring->char * 
}
  • 定义函数指针,解决重载问题
//定义函数指针
    void(Teacher::* teacherSingle)(QString) = &Teacher::hungry;
    void(Student::* studentSingle)(QString) = &Student::treat;
    //建立连接
    connect(te,teacherSingle,st,studentSingle);
//修改下课函数的触发信号
void Widget::classIsOver(){
    emit this->te->hungry("宫保鸡丁");
}
  • 运行测试

在这里插入图片描述

  • 再添加一个下课按钮,点击下课,自动触发
//定义下课按钮
QPushButton * btn = new QPushButton("下课",this);
connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);
  • 运行展示
    在这里插入图片描述

使用信号激活信号,再激活槽函数

  • 定义无参函数指针
    void(Teacher::* teacherSingle2)(void) = &Teacher::hungry;
    void(Student::* studentSingle2)(void) = &Student::treat;
  • 绑定按钮点击与老师饿了之间的关系
connect(btn,&QPushButton::clicked,this->te,teacherSingle2);
  • 绑定老师饿了与学生请吃饭之间的关系
connect(te,teacherSingle2,st,studentSingle2);
  • 运行测试
    在这里插入图片描述

扩展

  • 一个信号可以和多个槽相连
    如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
  • 多个信号可以连接到一个槽
    只要任意一个信号发出,这个槽就会被调用。
  • 一个信号可以连接到另外的一个信号
    当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
  • 槽可以被取消链接
    这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
  • 信号槽可以断开
    利用disconnect关键字是可以断开信号槽的
  • 槽函数支持使用Lambda 表达式
    QPushButton * btn2 = new QPushButton("切换",this);
    btn2->move(200,0);
    //如果接收对象是this,可以直接省略。
    //使用Lambda 表达式,有时候就可以在函数体内直接触发多个信号
    connect(btn2,&QPushButton::clicked,[=](){
        btn2->setText("success!");
    });
    
  • 使用Qt4版本写法
    connect(te,SIGNAL(hungry(QString)),st,SLOT(treat(QString)));
    看起来更加直观,不安全!
posted @ 2021-07-09 16:16  我就是隔壁老张  阅读(386)  评论(0编辑  收藏  举报