【Qt学习】02:信号和槽机制

信号和槽机制


OVERVIEW

  • 信号和槽机制
      • 一、系统自带信号与槽
      • 二、自定义信号与槽
        • 1.基本使用
          • student.cpp
          • teacher.cpp
          • widget.cpp
          • main.cpp
        • 2.信号与槽重载
          • student.cpp
          • teacher.cpp
          • widget.cpp
          • main.cpp
        • 3.信号连接信号
        • 4.Lambda表达式
        • 5.信号与槽总结

信号槽机制是 Qt 框架引以为豪的机制之一。

所谓信号槽实际就是观察者模式,将要处理的信号自己的一个函数/槽slot绑定来处理信号,当某个事件发生之后检测对象就会发出信号(无目的的广播),如果有其他对象对这个信号感兴趣就会使用connect函数进行连接。

即当信号发出时,被连接的槽函数会自动被回调。类似观察者模式:当发生了感兴趣的事件,某个操作就会被自动触发。

一、系统自带信号与槽

信号槽的优点:松散耦合,信号发送方和接收方本身是没有关联的,通过connect连接将两端耦合在一起

QPushButton * quitBtn = new QPushButton("quit",this);//创建关闭按钮
connect(quitBtn, &QPushButton::clicked, this, &QWidget::close);//信号槽使用方式

函数connect(sender, signal, receiver, slot);参数解释:

  • sender:发出信号的对象
  • signal:发送对象发出的信号
  • receiver:接收信号的对象
  • slot:接收对象在接收到信号之后所需要调用的函数(槽函数)

利用帮助文档了查找系统自带的信号和槽,在帮助文档中如按钮的点击信号输入QPushButton,首先我们可以在Contents中寻找关键字 signals信号的意思,但是我们发现并没有找到,这时候我们应该想到也许这个信号的被父类继承下来的,因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个。

这里的clicked就是我们要找到,槽函数的寻找方式和信号一样,只不过他的关键字是slot。

二、自定义信号与槽

使用connect可使用系统提供的信号和槽,

但Qt的信号槽机制并不仅仅是使用系统提供那部分,其还允许开发者设计自己的信号和槽,

1.基本使用

student.cpp
#ifndef STUDENT_H
#define STUDENT_H#include <QObject>class Student : public QObject {Q_OBJECT
public:explicit Student(QObject *parent = nullptr);
signals:public slots://槽函数slot 返回值是void 需要声明需要实现 可以有参数(发生重载)void treat();
};#endif // STUDENT_H
#include "student.h"
#include <QDebug>Student::Student(QObject *parent) : QObject(parent) { }void Student::treat() {qDebug() << "debug: receive signal. student treats teacher.";
}
teacher.cpp
#ifndef TEACHER_H
#define TEACHER_H#include <QObject>class Teacher : public QObject {Q_OBJECT
public:explicit Teacher(QObject *parent = nullptr);
signals://信号signal 返回值是void 只需要声明不需要实现 可以有参数(发生重载)void hungry();
public slots:};#endif // TEACHER_H
#include "teacher.h"Teacher::Teacher(QObject *parent) : QObject(parent) { }
widget.cpp
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include "teacher.h"
#include "student.h"QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECT
public:Widget(QWidget *parent = nullptr);~Widget();void classIsOver();
private:Ui::Widget *ui;Teacher *tch;Student *stu;
};#endif // WIDGET_H
#include <QPushButton>
#include <QDebug>
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//1.第一次绑定 使用系统自带信号与槽函数QPushButton *mybtn = new QPushButton("myBtn", this);mybtn->resize(100, 30);connect(mybtn, &QPushButton::clicked, this, &Widget::classIsOver);//按钮触发classIsOver函数//2.第二次绑定 使用自定义信号与槽函数tch = new Teacher(this);//初始化老师对象stu = new Student(this);//初始化学生对象connect(tch, &Teacher::hungry, stu, &Student::treat);//老师饿了时学生主动请客的connect连接
}Widget::~Widget() {delete ui;
}void Widget::classIsOver() {qDebug() << "debug: btn is pushed and Widget::classIsOver was called. emitting signal...";emit tch->hungry();//函数被调用 则触发发送老师饿了的信号
}
main.cpp
#include <QApplication>
#include "widget.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec();
}

在这里插入图片描述

2.信号与槽重载

student.cpp
#ifndef STUDENT_H
#define STUDENT_H#include <QObject>class Student : public QObject {Q_OBJECT
public:explicit Student(QObject *parent = nullptr);
signals:public slots://槽函数slot 返回值是void 需要声明需要实现 可以有参数(发生重载)void treat();void treat(QString food);
};#endif // STUDENT_H
#include "student.h"
#include <QDebug>Student::Student(QObject *parent) : QObject(parent) { }void Student::treat() {qDebug() << "debug: receive signal. student treats teacher.";
}void Student::treat(QString food) {qDebug() << "debug: receive signal. student treats teacher with " << food.toUtf8().data();
}
teacher.cpp
#ifndef TEACHER_H
#define TEACHER_H#include <QObject>class Teacher : public QObject {Q_OBJECT
public:explicit Teacher(QObject *parent = nullptr);
signals://信号signal 返回值是void 只需要声明不需要实现 可以有参数(发生重载)void hungry();void hungry(QString food);
public slots:};#endif // TEACHER_H
#include "teacher.h"Teacher::Teacher(QObject *parent) : QObject(parent) { }
widget.cpp
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include "teacher.h"
#include "student.h"QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECT
public:Widget(QWidget *parent = nullptr);~Widget();void classIsOver1();void classIsOver2();
private:Ui::Widget *ui;Teacher *tch;Student *stu;
};#endif // WIDGET_H
#include <QPushButton>
#include <QDebug>
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//1.第一次绑定 使用系统自带信号与槽函数QPushButton *mybtn1 = new QPushButton("myBtn1", this);mybtn1->resize(100, 30);connect(mybtn1, &QPushButton::clicked, this, &Widget::classIsOver1);//按钮触发classIsOver函数QPushButton *mybtn2 = new QPushButton("myBtn2", this);mybtn2->move(100, 0);mybtn2->resize(100, 30);connect(mybtn2, &QPushButton::clicked, this, &Widget::classIsOver2);//按钮触发classIsOver函数tch = new Teacher(this);//初始化老师对象stu = new Student(this);//初始化学生对象//2.第二次绑定 使用自定义信号与槽函数void(Teacher::*tchsignal_)() = &Teacher::hungry;void(Student::*stusignal_)() = &Student::treat;connect(tch, tchsignal_, stu, stusignal_);//老师饿了时学生主动请客的connect连接//3.第三次绑定 使用自定义信号与槽函数//定义函数指针 用于识别重载的函数地址void(Teacher::*tchsignal)(QString) = &Teacher::hungry;void(Student::*stusignal)(QString) = &Student::treat;connect(tch, tchsignal, stu, stusignal);
}Widget::~Widget() {delete ui;
}void Widget::classIsOver1() {qDebug() << "debug: btn1 is pushed and Widget::classIsOver1 was called. emitting signal...";emit tch->hungry();//函数被调用 则触发发送老师饿了的信号
}void Widget::classIsOver2() {qDebug() << "debug: btn2 is pushed and Widget::classIsOver2 was called. emitting signal...";emit tch->hungry("apple pie");
}
main.cpp
#include <QApplication>
#include "widget.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec();
}

在这里插入图片描述

3.信号连接信号

之前的使用中都是利用槽函数实现的,点击btn按钮触发函数调用,函数调用后emit发出信号再触发slot槽函数,从而实现连续的效果。

也可以使用另一种方式信号触发信号完成,利用点击btn按钮的信号去触发另一个信号,

主要对widget.cpp中的内容进行修改,修改后如下:

#include <QPushButton>
#include <QDebug>
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//1.第一次绑定 使用系统自带信号与槽函数QPushButton *mybtn1 = new QPushButton("myBtn1", this);mybtn1->resize(100, 30);//connect(mybtn1, &QPushButton::clicked, this, &Widget::classIsOver1);//按钮触发classIsOver函数QPushButton *mybtn2 = new QPushButton("myBtn2", this);mybtn2->move(100, 0);mybtn2->resize(100, 30);//connect(mybtn2, &QPushButton::clicked, this, &Widget::classIsOver2);//按钮触发classIsOver函数tch = new Teacher(this);//初始化老师对象stu = new Student(this);//初始化学生对象//2.第二次绑定 使用自定义信号与槽函数void(Teacher::*tchsignal_)() = &Teacher::hungry;void(Student::*stusignal_)() = &Student::treat;connect(tch, tchsignal_, stu, stusignal_);//老师饿了时学生主动请客的connect连接connect(mybtn1, &QPushButton::clicked, tch, tchsignal_);//信号连接信号//3.第三次绑定 使用自定义信号与槽函数//定义函数指针 用于识别重载的函数地址void(Teacher::*tchsignal)(QString) = &Teacher::hungry;void(Student::*stusignal)(QString) = &Student::treat;connect(tch, tchsignal, stu, stusignal);//connect(mybtn2, &QPushButton::clicked, tch, tchsignal);//信号连接信号connect(mybtn2, &QPushButton::clicked, this, [=](){emit tch->hungry("apple pie");});
}Widget::~Widget() {delete ui;
}void Widget::classIsOver1() {qDebug() << "debug: btn1 is pushed and Widget::classIsOver1 was called. emitting signal...";emit tch->hungry();//函数被调用 则触发发送老师饿了的信号
}void Widget::classIsOver2() {qDebug() << "debug: btn2 is pushed and Widget::classIsOver2 was called. emitting signal...";emit tch->hungry("apple pie");
}

在这里插入图片描述

btn触发信号连接信号,跳过了classIsOver函数的中间调用过程,直接实现了最终结果的输出。

4.Lambda表达式

C++11中的Lambda表达式用于定义并创建匿名的函数对象以简化编程工作,Lambda表达式的基本构成:

[capture](parameters)mutable->returnType {statement
}
  1. 函数对象参数;[],标识一个Lambda的开始,这部分必须存在不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:

    • void,没有使用任何函数对象参数。
    • =,函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)
    • &,函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)
    • this,函数体内可以使用Lambda所在类中的成员变量
    • a,将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符
    • &a,将a按引用进行传递
    • a, &b,将a按值进行传递,b按引用进行传递。
    • =,&a, &b,除a和b按引用进行传递外,其他参数都按值进行传递。
    • &, a, b,除a和b按值进行传递外,其他参数都按引用进行传递。
  2. 操作符重载函数参数;标识重载的 () 操作符的参数,没有参数时这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递

  3. 可修改标示符;mutable 声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)

    QPushButton * myBtn = new QPushButton (this);
    QPushButton * myBtn2 = new QPushButton (this);
    myBtn2->move(100,100);
    int m = 10;connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });
    connect(myBtn2,&QPushButton::clicked,this,[=] ()  { qDebug() << m; });
    qDebug() << m;
    
  4. 函数返回值;-> 返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。

  5. 是函数体;{},标识函数的实现这部分不能省略,但函数体可以为空。

利用Lambda表达式简化信号与槽的使用机制:利用lambda表达式实现点击按钮,关闭窗口:

QPushButton *btn = new QPushButton;
btn->setText("quit");
connect(btn, &QPushButton::clicked, this, [=](){this->close();
});

5.信号与槽总结

在这里插入图片描述

自定义信号槽需要注意的事项:

  1. 发送者和接收者都需要是QObject的子类(槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
  2. 自定义信号signal返回值是 void,只需要声明不需要实现,可以有参数(发生重载)
  3. 槽函数是普通的成员函数,需要声明也需要实现,会受到 public、private、protected 的影响(早期必须写到public slots作用域下)
  4. 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数

信号与槽的连接:connect函数

  1. 一个信号可以和多个槽函数相连:如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的
  2. 多个信号可以连接到一个槽:只要任意一个信号发出,这个槽就会被调用
  3. 一个信号可以连接到另外的一个信号:当第一个信号发出时第二个信号被发出,除此之外这种信号-信号的形式和信号-槽的形式没有什么区别。
  4. 信号槽要求信号和槽的参数类型对应(参数的个数信号可以多于槽函数,反之报错)
  5. 若信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
  6. 槽可以被取消链接:这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽
  7. 使用Lambda 表达式:在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的,在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。

QT4版本的信号槽写法:Qt5在语法上完全兼容Qt4。

connect(tch, SIGNAL(hungry(QString)), stu, SLOT(treat(QString)));

这里使用了SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。注意到connect函数的signal和slot都是接受字符串,一旦出现连接不成功的情况,Qt4是没有编译错误的(因为一切都是字符串编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/105900.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3+ts+uniapp小程序端自定义日期选择器基于内置组件picker-view + 扩展组件 Popup 实现自定义日期选择及其他选择

vue3ts 基于内置组件picker-view 扩展组件 Popup 实现自定义日期选择及其他选择 vue3tsuniapp小程序端自定义日期选择器 1.先上效果图2.代码展示2.1 组件2.2 公共方法处理日期2.3 使用组件 3.注意事项3.1refSelectDialog3.1 backgroundColor"#fff" 圆角问题 自我记…

软考A计划-系统集成项目管理工程师-项目风险管理-尾

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

windows系统服务器在不解锁屏幕不输入密码的前提下,电脑通电开机启动程序。

在控制面板中找到“管理工具”中的 “任务计划程序”&#xff0c;打开“任务计划程序”窗口。如图&#xff1a; 双击打开任务计划程序&#xff0c;空白出右键创建基本任务&#xff0c;或者点击最右侧的创建基本任务。 输入名称&#xff0c;点击下一步。 先选择计算机启动时&a…

2023年高教社杯 国赛数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…

Android进阶之路 - EditText输入字体自适应

遇到这么一个需求&#xff1a;“控件宽度有限&#xff0c;随着输入内容&#xff0c;动态修改字体大小”&#xff0c;如果是你&#xff0c;只如何来实现&#xff1f;又有几种方式&#xff1f; 嗯&#xff0c;就是这么一个简单的需求&#xff0c;让我记录了俩篇blog Android进阶…

用docker-compose搭建LNMP

docker-compose搭建LNMP 一、compose 的部署1.Docker Compose 环境安装 二、编写Docker Compose1.准备依赖文件,配置nginx2.配置mysql3.配置php4.编写docker-compose.yml5.执行6.查看 一、compose 的部署 &#xff08;1&#xff09;公司在实际的生产环境中&#xff0c;需要使用…

商品搜索网:连接您与各类商品的桥梁

导语&#xff1a;在如今信息爆炸的时代&#xff0c;购物已经不再是传统的实体店购买&#xff0c;而是通过互联网实现的线上购物方式。而要实现高效的线上购物&#xff0c;商品搜索引擎则成为我们的得力助手。作为国内垂直的商品搜索之一&#xff0c;为中国用户提供全面的数码电…

【Qt专栏】实现单例程序,禁止程序多开的几种方式

目录 一&#xff0c;简要介绍 二&#xff0c;实现示例&#xff08;Windows&#xff09; 1.使用系统级别的互斥机制 2.通过共享内存&#xff08;进程间通信-IPC&#xff09; 3.使用命名互斥锁&#xff08;不推荐&#xff09; 4.使用文件锁 5.通过网络端口检测 一&#xf…

Linux 下 Mysql 的使用(Ubuntu20.04)

文章目录 一、安装二、使用2.1 登录2.2 数据库操作2.2.1 创建数据库2.2.2 删除数据库2.2.3 创建数据表 参考文档 一、安装 Linux 下 Mysql 的安装非常简单&#xff0c;一个命令即可&#xff1a; sudo apt install mysql-server检查安装是否成功&#xff0c;输入&#xff1a; …

使用kubeadm方式快速部署一个K8S集群

目录 一、环境准备 二、环境初始化 三、在所有主机上安装相关软件 1、安装docker 2、配置k8s的yum源 3、安装kubelet、kubeadm、kubectl 四、部署Kubernetes Master 五、加入Kubernets Node 六、部署CNI网络插件 七、测试k8s集群 一、环境准备 我的是CentOS7系统&am…

iOS App签名与重签名:从开发者证书到重新安装运行

前文回顾&#xff1a; iOS脱壳技术&#xff08;二&#xff09;&#xff1a;深入探讨dumpdecrypted工具的高级使用方法 iOS逆向&#xff1a;越狱及相关概念的介绍 在本文中&#xff0c;我们将详细介绍iOS应用的签名过程&#xff0c;包括开发者证书的种类、证书与App ID、Provisi…

利用 Apifox 的 Mock 功能模拟常见业务数据的最佳方法

Apifox 拥有强大的 Mock 功能&#xff0c;兼容 Mock.js 语法的同时还提供 Nunjucks 和自定义脚本支持&#xff0c;能够满足不同场景需求。 今天给大家分享一些常见业务场景的 Mock 使用技巧&#xff0c;当然&#xff0c;实现的方法不唯一。在开始之前&#xff0c;你需要将 Api…

【C++】list

list 1. 简单了解list2. list的常见接口3. 简单实现list4. vector和list比较 1. 简单了解list list的底层是带头双向循环列表。因此list支持任意位置的插入和删除&#xff0c;且效率较高。但其缺陷也很明显&#xff0c;由于各节点在物理空间是不连续的&#xff0c;所以不支持对…

Python项目开发案例————学生信息管理系统(附源码)

一、学生信息管理系统 本文使用Python语言开发了一个学生信息管理系统&#xff0c;该系统可以帮助教师快速录入学生的信息&#xff0c;并且对学生的信息进行基本的增、删、改、查操作&#xff1b;还可以实时地将学生的信息保存到磁盘文件中。 1.1 需求分析 为了顺应互联网时代…

软件测试及数据分析处理实训室建设方案

一 、系统概述 软件测试及数据分析处理是软件开发过程中的一项重要测试活动&#xff0c;旨在验证不同软件模块或组件之间的集成与交互是否正常。综合测试确保各个模块按照设计要求正确地协同工作&#xff0c;以实现整个软件系统的功能和性能。以下是软件测试及数据分析处理的一…

SpringMVC程序开发

前言&#xff1a; &#x1f4d5;作者简介&#xff1a;热爱编程的小七&#xff0c;致力于C、Java、Python等多编程语言&#xff0c;热爱编程和长板的运动少年&#xff01; &#x1f4d8;相关专栏Java基础语法&#xff0c;JavaEE初阶&#xff0c;数据库&#xff0c;数据结构和算法…

【位运算】算法实战

文章目录 一、算法原理常见的位运算总结 二、算法实战1. leetcode面试题01.01. 判断字符是否唯一2. leetcode268 丢失的数字3. leetcode371 两整数之和4. leetcode004 只出现一次的数字II5. leetcode面试题17.19. 消失的两个数字 三、总结 一、算法原理 计算机中的数据都以二进…

香港服务器怎么打开SSH

​  SSH是一种远程登录协议&#xff0c;可以通过加密方式在网络上安全地传输数据。它允许用户在远程服务器上执行命令&#xff0c;管理文件和目录&#xff0c;并进行其他系统管理任务。 如何打开SSH服务? 1.确认已安装OpenSSH服务器&#xff1a; 你可以通过命令sudoapt-geti…

开发一款AR导览导航小程序多少钱?ar地图微信小程序 ar导航 源码

随着科技的不断发展&#xff0c;增强现实&#xff08;AR&#xff09;技术在不同领域展现出了巨大的潜力。AR导览小程序作为其中的一种应用形式&#xff0c;为用户提供了全新的观赏和学习体验。然而&#xff0c;开发一款高质量的AR导览小程序需要投入大量的时间、人力和技术资源…

Sql Server导出数据库到另一个数据库

1.打开sql server数据库&#xff0c;连接到服务器后&#xff0c;找到需要导出的数据库&#xff0c;右击后选择 任务->导出数据。 2.点击 下一步。 3.身份验证可以使用SQL Server身份验证&#xff0c;就是当时建立连接时的用户名和密码&#xff0c;数据库名称使用默认的&…