Qt-信号与槽

news2024/9/20 11:02:43

1. 认识信号和槽

Qt中,谈到信号,涉及到三个要素.

  • 信号源:由哪个控件发出的信号
  • 信号的类型:用户进行不同的操作,就可能出发不同的信号
    点击按钮,触发点击信号
    在输入框中移动光标,触发移动光标的信号
    勾选一个复选框
    选择一个下拉框,都会触发出不同的信号
  • 信号的处理方式:槽(slot)=> 函数
    Qt中可以使用 connect 这样的函数,把一个信号和一个槽关联起来,后续只要信号触发了,Qt就会自动的执行槽函数。
    所谓的 “槽函数” 本质上也是一种 “回调函数”(callback)

回调函数:

  1. 最早 C 语言阶段
    C 进阶 =>指针进阶 =>函数指针
    1)实现转移表,降低代码的"圈复杂度"
    2)实现回调函数效果 =>qsort
  2. C++ 阶段
    1)STL 中,函数对象/仿函数.
    2)lambda 表达式
  3. Linux 中
    1)信号处理函数
    2)线程的入口函数.
    3)epol 基于回调的机制

2. Connect函数

在 Qt 中,QObject 类提供了⼀个静态成员函数 connect() ,该函数专⻔⽤来关联指定的信号函数和槽函数

connect函数的原型

// connect函数的原型
connect (const QObject *sender,// 描述了当前信号是哪个控件发出来的
		const char * signal ,// 信号的类型
		const QObject * receiver ,// 哪个对象负责处理
		const char * method ,// 这个对象怎么处理
		Qt::ConnectionType type = Qt::AutoConnection ) // 这个暂时不考虑

参数说明:

  • sender:信号的发送者;
  • signal:发送的信号(信号函数);
  • receiver:信号的接收者;
  • method:接收信号的槽函数;
  • type:⽤于指定关联⽅式,默认的关联⽅式为 Qt::AutoConnection,通常不需要⼿动设定。

举一个实例:(此代码在Qt Creator上运行)

// Widget.cpp
#include "widget.h"
#include "ui_widget.h"

#include<QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 按钮实现关闭窗口
    QPushButton*button=new QPushButton(this);
    button->setText("关闭");
    button->move(400,300);

    connect(button,&QPushButton::clicked,this,&Widget::close);

}

Widget::~Widget()
{
    delete ui;
}
//  Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

在这里插入图片描述
调用和声明的函数参数不匹配,这是由于他显示的是老版本的,新版本中会有重载函数匹配
在这里插入图片描述

3. 自定义槽函数

所谓的自定义一个 槽函数,操作过程和自定义一个普通的成员函数,没啥区别!!
在以前版本的 Qt 中,槽函数必须放到

public/private/protected slots:

这里是引用
此处的 slots 是 Qt 自己扩展的关键字,(不是 C++ 标准中的语法)
Qt 里广泛使用了 元编程 技术.(基于代码,生成代码)qmake 构建 Qt 项目的时候, 就会调用专门的扫描器, 扫描代码中特定的关键字.(slots 这种)基于关键字自动生成一大堆相关的代码.

举一个例子:按下按钮,更改窗口的标题。(Qt)

// Widget.cpp
#include "widget.h"
#include "ui_widget.h"

#include<QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    QPushButton* button=new QPushButton(this);
    button->setText("按钮");

    // 自定义槽函数1
    connect(button,&QPushButton::clicked,this,&Widget::handleClick);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleClick()
{
    // 按下按钮,更改窗口的标题
    this->setWindowTitle("按钮已经按下!");


}
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();


public slots:// 再之前版本中,自定义的槽函数要声明写在相应的下面
    void handleClick();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

第二种方式自定义槽函数
在这里插入图片描述
这个窗口就列出了 QPushButton 给我们提供的所有的信号(还包含了 QPushButton 父类的信号)
这种槽函数不用去用connect函数建立信号与槽函数的连接

按下按钮,修改窗口的标题
在这里插入图片描述

// WIdget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


}

Widget::~Widget()
{
    delete ui;
}

// 连接信号槽(注意:名字不可以更改)
// 这个名字是    objectName  信号的名字
void Widget::on_pushButton_clicked()
{
    this->setWindowTitle("按钮已经按下!");
}
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

运行结果:
在这里插入图片描述
如果我们通过图形化界面创建控件,还是推荐使用这种快速的方式来连接信号槽.
如果我们是通过代码的方式来创建控件,还是得手动 connect.(你的代码中没有调用 connectSlotsByName)

4. 自定义信号

这里是引用
所谓的 Qt 的信号,本质上也就是一个"函数"
Qt 5 以及更高版本中,槽函数和普通的成员函数之间,没啥差别了
但是,信号, 则是一类非常特殊的函数.

  1. 程序员只要写出函数声明,并且告诉 Qt,这是一个"信号"即可.这个函数的定义,是 Qt 在编译过程中,自动生成的,(自动生成的过程,程序员无法干预)
  2. 作为信号函数,这个函数的返回值,必须是 void有没有参数都可以. 甚至也可以支持重载,

在这里插入图片描述
这个也是 Qt 自己扩展出来的关键字~~
qmake 的时候, 调用一些代码的分析/生成工具
扫描到类中包含 signas 这个关键字的时候,此时,就会自动的把下面的函数声明认为是信号,并且给这些信号函数自动的生成函数定义

无参数的信号和槽
在这里插入图片描述
在这里插入图片描述

5. 带参数的信号和槽

信号和槽 也可以带参数
当信号带有参数的时候,槽的参数必须和信号的参数一致
此时发射信号的时候,就可以给信号函数传递实参,与之对应的这个参数就会被传递到对应的槽函数中.

举个例子,有参数的信号和槽,进行点击哪个按钮执行哪个动作

// Widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 只是建立信号与槽的连接,并没有发出信号
    // 无参的
    //connect(this,&Widget::mySignal,this,&Widget::handlemySingnal);

    // 带参数的
    connect(this,&Widget::mySignal2,this,&Widget::handlemySingnal);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::handlemySingnal(const QString& Text)
{
    // this->setWindowTitle("处理自定义信号");
    this->setWindowTitle(Text);
}




void Widget::on_pushButton_clicked()
{
    // 发出信号
    // 发送信号,可以在任何适合的代码中

    // 按下按钮之后,则就进行发送自定义信号
    //emit mySignal();// 其实emit在Qt5中现在啥都没做
    // 真正的操作都包含在mySingal信号中

    // mySignal();// 这种方式也是可以的


    emit mySignal2("把标题设置为标题1");
}

void Widget::on_pushButton_2_clicked()
{
    emit mySignal2("把标题设置为标题2");
}

// Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void handlemySingnal(const QString& Text);// 处理信号的自定义槽函数

signals:// Qt会自己识别关键字。自定义信号
    //void mySignal();

    // 带参数的信号
    void mySignal2(const QString& Text);

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

直观的思考,应该是要求信号的参数个数和槽的参数个数,严格一致~~此处为啥允许信号的参数比槽的参数多呢??
一个槽函数,有可能会绑定多个信号
如果我们严格要求参数个数一致,就意味着信号绑定到槽的要求就变高了换而言之,当下这样的规则,就允许信号和槽之间的绑定更灵活了更多的信号可以绑定到这个槽函数上了

槽和信号是可以多对多的关系
Qt 信号槽,connect 这个机制, 设想很美好的
1)解耦合.把触发 用户操作的控件 和 处理对应用户的操作逻辑 解耦合
2)“多对多” 效果
一个信号, 可以 connect 到多个槽函数上,一个槽函数,也可以被多个信号 connect.

6. 信号和信号槽存在的意义

7. 信号与槽断开连接

使用 disconnect 来断开信号槽的连接
disconnect 使用的方式和 connect 是非常类似的,
disconnect用的比较少的.
大部分的情况下,把信号和槽连上了之后,就不必管了
主动断开往往是把信号重新绑定到另一个槽函数上~

定义两个按钮,使之按下第一个按钮窗口标题会有变化,在按下第二个按钮直接,会断开按钮1和槽函数的联系,之后按钮1与另一个槽函数建立联系,之后按下按钮1窗口标题会发生与之前不一样的变化。

  1. 第一步,在这个页面进行,如图,在右键按钮2机型 转移槽 的操作
    在这里插入图片描述
    第二部:编写代码
// widget.cpp
#include "widget.h"
#include "ui_widget.h"

#include<QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 断开槽和信号之间的联系的练习

    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleClick()// 原先的槽函数
{
    this->setWindowTitle("修改窗口的标题");
}

void Widget::handleClick2()// 断开修改之后的槽函数
{
    this->setWindowTitle("修改窗口的标题2");
}


// 按下按钮2,切换槽函数
void Widget::on_pushButton_2_clicked()
{
    // 1.先断开当前的槽和信号的连接
    // 如果没有disconnect,则就会构成一个信号绑定两个槽函数,触发信号的时候,两个槽函数都会执行
    //disconnect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
    // 会造成一对多的关系

    // 2.之后进行之后的槽和信号的连接,重新绑定信号槽
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick2);

}
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    // 处理的自定义槽函数
    void handleClick();// 原先的槽函数

    void handleClick2();// 要修改的槽函数

private slots:

    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

8. 用lambda表达式也可以定义槽函数

定义槽函数的时候,也是可以使用 lambda 表达式的!!

举例

// Widget.cpp
#include "widget.h"
#include "ui_widget.h"

#include<QPushButton>
#include<QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 用lambda表达式进行编写槽函数

    QPushButton* mybutton=new QPushButton(this);
    mybutton->setText("按钮");
    mybutton->move(200,200);

    // 由于lambda表达式默认是不会有外层作用域的数据,要进行捕获列表捕获
    connect(mybutton,&QPushButton::clicked,this,[=]{
        qDebug()<<"lambda表达式被执行了";
        this->move(100,200);
        mybutton->move(500,500);
    });

}

Widget::~Widget()
{
    delete ui;
}
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

运行结果:
在这里插入图片描述

解决lambda表达式无法从上层获取作用域中的变量

在这里插入图片描述
指针变量按照值传递或者引用来传递,都无所谓
lambda 除了可以按照 值的方式来捕获变量 [=] 还可以按照引用的方式来捕获 [&](Qt 中很少这么写)捕获到的变量一般就是各种控件的指针
如果按引用,还得更关注这个引用的变量本身的生命周期

9. 查看内置信号和槽

系统⾃带的信号和槽通常是通过" Qt 帮助⽂档"来查询

10. 总结

  1. 信号槽是啥
    1)信号源
    2)信号的类型
    3)信号的处理方式
  2. 信号槽 使用
    connect
  3. 如何查阅文档.
    一个控件,内置了哪些信号,信号都是何时触发
    一个控件,内置了哪些槽,槽都是什么作用.
    很有可能需要的信号槽,还得到这个类的父类/爷爷类/祖宗类去进行査询~~
  4. 自定义槽函数
    本质上就是自定义一个普通的成员函数
    还可以让 Qt Creator 自动生成.(虽然没有显式 connect,但是可以通过函数名字特定规则来完成自动连接)
  5. 自定义信号
    信号本质就是成员函数.(函数的定义是 Qt 自己生成的,咱们只需要写函数声明)signals: 自定义关键字中,
    emit 来完成信号的发射(emit 也可以省略)
  6. 信号和槽还可以带有参数.发射信号的时候,把参数传递给对应的槽信号的参数和槽的参数要一致
    1)类型匹配
    2)个数,信号的参数要多于槽的参数
  7. 信号槽存在的意义
    解耦合
    多对多效果.(非常类似于 mysql 中的多对多的)
    演示了信号槽多对多连接的情况~~
  8. disconnect 使用方式
  9. lambda 表达式, 简化槽函数的定义

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

github私有仓库通过action部署hexo到公开仓库

github私有仓库通过action部署hexo到公开仓库 有一段时间一直将博客md文件直接放到公开仓库然后通过工作流action创建一个gh-page分支&#xff0c;来实现部署 但是这样做有一个问题&#xff0c;如果你的源文件&#xff0c;或者配置文件中有涉及变量&#xff0c;或者密钥key&a…

STM32G474之TIM1输出PWM互补信号(无死区时间和BKIN输入)

STM32G474之TIM1输出PWM互补信号&#xff0c;无死区时间&#xff0c;无BKIN输入。定时器1是16向上计数器&#xff0c;16向下计数器&#xff0c;16向上/向下计数器&#xff0c;输入时钟分频值&#xff1a;“1至65536”中的任意整数&#xff1b;捕获输入通道4个&#xff0c;比较输…

【C++ Primer Plus习题】9.4

问题: 解答: main.cpp #include <iostream> #include "sales.h" using namespace std; using namespace SALES;int main() {Sales s1, s2;double de[QUARTERS] { 12.1,32.1,42.1,51.1 };setSales(s1, de, QUARTERS);showSales(s1);cout << endl;setSal…

AI图片扩展工具 | 一名非技术人员依靠AI就能写出来

前言 我本职是一个技术支持&#xff0c;原本和开发搭不到边。但这两年 AI 发展迅猛&#xff0c;让我这样的半吊子也能借助 AI 的力量写网站。 因为这两年压力大&#xff0c;所以琢磨着出海看看能不能挣到钱&#xff0c;所以在学习做网站。 这个站是我的第 5 个作品了。前面 …

盘点大模型中转 API 平台,并比较费用

1. 大模型中转 API 平台集合 1.1 DevAGI DevAGI开放平台 Open AI 价格 1.2 Deepbricks 官网价格 1.3 AiHubMix AiHubMix 官网 使用教程 价格&#xff1a; 1.4 WildCard 开卡订阅 WildCard官网 价格 有3.5% 的充值手续费&#xff0c;API 价格与 Open AI 一样 2. 价…

vue3 element-plus form 表单 循环校验

需求&#xff1a; 表单是循环出来的&#xff0c;3个一组&#xff0c;每组对于前端来说是一样的&#xff0c; 需要校验输入框是否必填是否为小数或者整数 效果&#xff1a; 未输入--显示&#xff1a;请输入 输入不是小数或整数--显示&#xff1a;请输入整数或小数 输入正确…

南京大学软件学院硕士毕业流程

背景介绍 南京大学作为国内顶尖的985高校&#xff0c;拥有丰富的校内资源和雄厚的师资力量。然而&#xff0c;在管理与协调方面仍存在一定的不足。尤其是在硕士生培养过程中&#xff0c;临近毕业阶段的流程中常出现信息不透明和混乱的现象&#xff0c;导致学生在了解所需材料和…

codeforces Round 970 (Div. 3)(A-F)

文章目录 [Codeforces Round 970 (Div. 3)](https://codeforces.com/contest/2008)A-[Sakurakos Exam](https://codeforces.com/contest/2008/problem/A)B-[Square or Not](https://codeforces.com/contest/2008/problem/B)C-[Longest Good Array](https://codeforces.com/cont…

Halcon!!!最新!! 从零认识标定板——制作描述文件和自己的标定板

一.标定板简介 ‌标定板是一种带有固定间距图案阵列的几何模型&#xff0c;主要用于机器视觉、图像测量、摄影测量、三维重建等领域。‌它的主要功能包括校正镜头畸变、确定物理尺寸和像素间的转换关系&#xff0c;以及确定空间物体表面某点的三维几何位置与其在图像中对应点之…

C练手题--Progressive Spiral Number Position 【7 kyu】

一.原题 链接&#xff1a;Training on Progressive Spiral Number Position | Codewars Assume that you started to store items in progressively expanding square location, like this for the first 9 numbers: 二、解题 1、分析 &#xff08;1&#xff09;数字必须存…

自闭症自言自语会好吗

在自闭症儿童的成长过程中&#xff0c;自言自语作为一种常见的行为表现&#xff0c;往往让家长们既担忧又困惑。这种非社交性的语言行为&#xff0c;虽然在一定程度上是自闭症孩子自我交流的方式&#xff0c;但过度或不适宜的自言自语却可能影响其社交技能和日常功能的发展。那…

[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-25 RGB转HDMI显示方案

软件版本&#xff1a;VIVADO2021.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA 实验平台&#xff1a;米联客-MLK-H3-CZ08-7100开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 http…

3.6 逻辑运算

&#x1f393; 微机原理考点专栏&#xff08;通篇免费&#xff09; 欢迎来到我的微机原理专栏&#xff01;我将帮助你在最短时间内掌握微机原理的核心内容&#xff0c;为你的考研或期末考试保驾护航。 为什么选择我的视频&#xff1f; 全程考点讲解&#xff1a;每一节视频都…

招聘系统开发前景分析

招聘系统的前景分析可以从多个维度进行&#xff0c;包括市场需求、技术趋势、竞争格局以及未来发展趋势等方面。 一、市场需求 持续增长的市场规模&#xff1a;随着全球经济的复苏和数字化转型的加速&#xff0c;企业对高效招聘解决方案的需求不断增加。根据市场数据&#xff…

青书学堂 看视频 看课时 php 懒人版

上一篇关于青书学堂的 操作起来 有点麻烦 趁这几天有时间 优化了一下 建议php 7.3 版本 (本程序会用到php里的curl 模块 记得打开) 如果运行时 获取信息空白(https 容易出现) 可以测试一下自己php的curl能不能正常用 如果不能可以参考一下我的另一篇文章 SSL rtificate …

从0开始深度学习(3)——概率

1 基本概率论 大数定律&#xff08;law of large numbers&#xff09;&#xff1a;随着投掷次数的增加&#xff0c;这个估计值会越来越接近真实的潜在概率。从概率分布中抽取样本的过程称为抽样&#xff08;sampling&#xff09;将概率分配给一些离散选择的分布称为多项分布&a…

【大模型实战篇】RoPE旋转位置编码PyTorch代码分析

1. 背景介绍 之前我们通过两篇技术文章《LLaMA3结构关键模块分析》和《RoPE旋转位置编码底层数学原理分析》对旋转位置编码RoPE的原理进行了必要的讲解。接下来&#xff0c;我们将针对来自rotary-embedding-torch【1】开源库的实现&#xff0c;对RoPE的PyTorch代码实现进行分…

前后端分离的security角色权限实现

本案例是使用SpringBoot2.7.6securityMyBatis-plusVue2axios实现 一、security简介 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架&#xff0c;专为Java应用程序设计。 &#xff08;1&#xff09;基本功能 身份验证&#xff08;Authentication&#x…

关于安装MySQL遇到的问题

数据库相关概念 &#x1f4a1;数据库系统 &#xff08; DataBase System&#xff0c; 简称 DBS&#xff09; 是指计算机系统引入数据库后的系统构成&#xff0c; 是一个具有管理数据库功能的计算机软硬件综合系统。 数据库系统可以实现有组织地、动态地存储大量数据、提供数…

【补-办公室】拟批语的区别

拟批语 常见拟批语 批示、审示、阅示、核注 审批、审核、审阅、审定&#xff08;订&#xff09;、审发、审议、审处、阅改、阅知、阅研、阅处、研提、研办、研复、核&#xff08;转&#xff09;报、核示、核批、批办等 阅示和审示 区分是收文还是发文 发文审&#xff0c;收文阅…