C++qt-信号-信号槽

news2024/12/23 14:01:57

1、概念

信号和槽是两种函数,这是Qt在C++基础上新增的特性,类似于其他技术中的回调的概念。

信号和槽通过程序员提前设定的“约定”,可以实现对象之间的通信,有两个先决的条件:

  • 通信的对象必须都是从QObject类中派生出来的。
  • 类中要有Q_OBJECT宏。
  • 2、函数原型

信号槽需要在使用前进行“约定”,这个约定被称为连接。

【例子】:如果金龙考试考了100分,新宇请金龙吃饭。

// 参数1:const QObject * sender 发射者,表示因发起对象
// 参数2:const char * signal信号函数,表示因的发起动作,使用SIGNAL()包裹。
// 参数3:const QObject * receiver 接收者,表示果的发起对象
// 参数4:const char * method 槽函数,表示果发起的动作,请吃饭,SLOT()包裹。
QObject:: connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method)[static]

3、实现

为了学习,把信号槽分为三种实现方式。

  • 自带信号 -> 自带槽
  • 自带信号 -> 自定义槽
  • 自定义信号

3.1 自带信号->自带槽

这种连接方式是最简单的,因为信号函数和槽函数都是Qt内置的,只需要在文档中查询出函数后,使用connect函数连接即可。

查找函数 F1F1 查找手册

查找函数:在对应的QPushButton Class基类继承自(QAbstractButton)类,查找自带槽(Public Slots),找到对应槽函数(click),

// 按钮按下后时发射的信号
void QAbstractButton:: clicked(bool checked = false)[signal]

查询函数:在对应的Widget基类查找自带槽(Public Slots),找到对应槽函数(close),

// 关闭 槽函数
bool QWidget:: close()

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QDebug>
#include <QPushButton>


class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
    QPushButton *btn;   // 成员变量
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    //设置窗口的宽高
    resize(500,500);
    btn = new QPushButton("关闭",this);
    // 设置按钮的位置
    btn->move(200,250);
    //but 发起者、SIGNAL()包裹发起动作:点击、this 接收者、SLOT(close()) 接收结果 close关闭窗口
    connect(btn,SIGNAL(clicked()),this,SLOT(close()));
}
Dialog::~Dialog()
{
    // C++内存回收
    delete btn;
}

3.2 自带信号->自定义槽

Qt不可能内置所有执行的动作代码,特别是一些复杂的操作,需要开发者手动编写槽函数。这种方式也是所有连接方式中使用最多的。

槽函数时一个特殊的成员函数,在声明的时候权限的作用主要是修饰其作为普通成员函数的使用效果,不影响信号槽的连接效果。

【例子】:点击按钮,向右边和下面移动窗口10个像素。同时输出当前窗口的坐标。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H
// 添加头文件QDialog对话框基类,Qt自带类型通常使用Q开头
#include <QDialog>
#include <QDebug>
#include <QPushButton>
// 自定义对话框类
// 继承于QDialog类
class Dialog : public QDialog
{
    // 是一个宏是必要条件:一个标准,有这个宏才可以用connect链接
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);    // 构造函数
    ~Dialog();      // 析构函数
    QPushButton *btn;   // 成员变量
    //声明自定义槽函数
private slots://最小权限法则,能使用私有权限就是用私有(固定写法:表示声明的是一个槽函数,connect连接时才能找到该槽函数)
    void mySlot();//小驼峰命名:第一个单词首字母小写,其他大写
};
#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
// 构造函数定义
// parent 参数
Dialog::Dialog(QWidget *parent): QDialog(parent)   // 透传构造
{
    //设置窗口宽高
    resize(500,500);
    btn = new QPushButton("移动",this);
    //设置按钮位置
    btn->move(200,200);
    //but 发起者、SIGNAL(clicked()) 发起动作:点击、this 接收者、SLOT(close()) 接收结果 close关闭窗口
    connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
}
// 析构函数类外定义
Dialog::~Dialog()
{
    // C++内存回收
    delete btn;
}
//槽函数定义
void Dialog::mySlot()
{
    //先获取当前窗口坐标
    int x = this->x();//坐标函数返回值就是坐标值
    int y = this->y();
    //移动坐标位置,每次获取的都是最新的坐标位置,所以不需要赋值
    move(x+10,y+10);
    //输出当前坐标位置
    qDebug()<<x+10<<y+10;
}

3.3 自定义信号

emit关键字发射。

为了讲解,强行使用自定义信号,并非问题的最优解,主要学习写法。

信号函数是一个非常特殊的函数,因此只有声明,没有定义,没有函数体。因此无法调用,只能使用emit关键字发射。

【例子】点击按钮,关闭窗口。

3.1 节的信号连接方式

本节强行在中间加一层自定义信号的转发过程。

上图中表示信号槽连接。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
private:
    QPushButton *btn;
//自定义槽函数声明
private slots:
    void mySlot();
//自定义信号声明
signals:
    void mySignal();
};
#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(500,500);
    btn = new QPushButton("杀鸡用牛刀",this);
    btn->move(200,200);
    //第一次信号槽链接
    connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
    //第二次信号槽链接
    connect(this,SIGNAL(mySignal()),this,SLOT(close()));
}
Dialog::~Dialog()
{
    //释放类对象堆空间
    delete btn;
}
void Dialog::mySlot()
{
    //发射信号
    emit mySignal();
}

步骤:自定义信号无法调用,只能发射信号,因此在头函数内声明自定义信号和自定义槽函数,在自定义槽函数内发射该自定义信号,并调用相关槽函数。(声明自定义槽函数和自定义信号,需要connect连接多次)

4、信号槽传参

【例子】点击按钮,按钮上显式点击的次数。

// QPushButton的文字属性为text:QString,可以使用setText更改按钮文字
// 参数:更新的文字
void	setText(const QString & text)

正常解法(非信号槽传参):

dialog.h

#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
    QPushButton *btn;
    //自定义槽函数声明
private slots:
    void mySlot();
};
#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(500,500);
    //btn初始化 初始数据为”0“
    btn = new QPushButton("0",this);
    btn->move(200,200);
    //connect 连接
    connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
}
Dialog::~Dialog()
{
    delete btn;
}
void Dialog::mySlot()
{
    //静态局部变量
    static int count = 0;
    count++;
    //类型转换 int-> QString,整形转字符型,number(形参):形参:要转换的数据
    QString text = QString::number(count);
    btn->setText(text);//更改按钮文字
}

信号槽传参法:

把上面的案例强行改为信号槽传参:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
    QPushButton *btn;
    //自定义槽函数声明
private slots:
    void mySlot();
    void mySlot2(int);//有参自定义槽函数
signals:
    void mySignal(int);//有参自定义信号函数
};
#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(500,500);
    btn = new QPushButton("0",this);
    btn->move(200,200);
    //connect 连接
    connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
    connect(this,SIGNAL(mySignal(int)),this,SLOT(mySlot2(int)));
}
Dialog::~Dialog()
{
    delete btn;
}
//自定义信号槽初始化
void Dialog::mySlot()
{
    //静态局部变量
    static int count = 0;
    count++;
    //发射带参数的自定义信号函数
    emit mySignal(count);
}
//自定义信号槽函数初始化,注意传参
void Dialog::mySlot2(int count)
{
    //类型转化 int -> QString
    QString text = QString::number(count);
    btn->setText(text);
}

需要注意的是:

  • 理论上可以传递任意多个参数,建议最多写两个参数,多了会很冗余。如果非得传多个参数的话,可以定义成一个类,传递对象。
  • 信号的参数个数必须大于等于槽的参数个数。
  • 信号的参数类型要与槽的参数类型匹配。

5、对应关系

5.1 一对多

一对多指的是一个信号连接多个槽函数。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
#include <QPushButton>
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
    QPushButton *btn0;
    QPushButton *btn1;
    QPushButton *btn2;
private slots:
    void mySlot0();
    void mySlot1();
    void mySlot2();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(500,500);
    btn0 = new QPushButton("一对多",this);
    btn0->move(200,250);
    //一对多的优势是可以灵活处理每个对应关系
    //例如可以断开某个信号槽连接
    //断开连接的函数与连接函数传参一样,只需要在前面加一个dis
    //disconnect(btn0,SIGNAL(clicked()),this,SLOT(mySlot0()));
    //一对多信号槽连接
    connect(btn0,SIGNAL(clicked()),this,SLOT(mySlot0()));
    connect(btn0,SIGNAL(clicked()),this,SLOT(mySlot1()));
    //一对一信号槽链接,连接简单,但处理不灵活
    btn1 = new QPushButton("一对一",this);
    btn1->move(100,200);
    connect(btn1,SIGNAL(clicked()),this,SLOT(mySlot2()));
}
Dialog::~Dialog()
{
    delete btn0;
}
void Dialog::mySlot0()
{
    qDebug()<<"A";
}
void Dialog::mySlot1()
{
    qDebug()<<"B";
}
void Dialog::mySlot2()
{
    mySlot0();
    mySlot1();
}

在头文件内声明的函数,可以以下操作在主函数内自定添加定义

5.2 多对一

多对一指的是多个信号连接同一个槽函数。多对一的问题在于槽函数无法直接判断那个信号触发的槽函数调用,可以通过sender函数在槽函数中获得发射者对象,通过对象对比判断来源。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
#include <QDebug>
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
    QPushButton *btn1;
    QPushButton *btn2;
private slots:
    void btnClickedSlot();
};
#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(500,500);
    btn1 = new QPushButton("多对一A",this);
    btn1->move(200,200);

    btn2 = new QPushButton("多对一B",this);
    btn2->move(300,300);

    // 多对一连接
    connect(btn1,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
    connect(btn2,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}
Dialog::~Dialog()
{
    delete btn1;
    delete btn2;
}
void Dialog::btnClickedSlot()
{
    // 通过sender函数获取发射者对象
    if(sender() == btn1)
    {
        qDebug() << "A" ;
    }
    else if(sender() == btn2)
    {
        qDebug() << "B" ;
    }
    else
    {
        qDebug() << "对象错误" ;
    }
}

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

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

相关文章

threejs 光带扩散动画

目录 一、创建光带 (1) 设置光带顶点 (2) 设置光带顶点透明度属性 二、光带动画 完整代码 html文件代码 js文件代码 最后展示一下项目里的效果&#xff1a; 最近项目中要求做一段光带效果动画&#xff0c;尝试着写了一下&#xff0c;下面是本次分享光带扩散动画的效果预…

地铁判官(外包)

到处都是说外包不好不好的&#xff0c;从没有想过自身问题。 例如&#xff1a; 技术人员动不动就是说&#xff0c;进了外包三天&#xff0c;一年&#xff0c;三年之后技术退步很多。就算你这样的人进了甲方&#xff0c;也是个渣渣。(声明一下&#xff0c;我也是外包&#xff0…

CMU15-445-Spring-2023-Project #2 - 前置知识(lec07-010)

Lecture #07_ Hash Tables Data Structures Hash Table 哈希表将键映射到值。它提供平均 O (1) 的操作复杂度&#xff08;最坏情况下为 O (n)&#xff09;和 O (n) 的存储复杂度。 由两部分组成&#xff1a; Hash Function和Hashing Scheme&#xff08;发生冲突后的处理&…

阿里云99元一年2核2G3M云服务器值得买吗?

阿里云作为国内领先的云服务提供商&#xff0c;一直致力于为用户提供优质、高效的服务。目前&#xff0c;阿里云推出的99元一年2核2G3M云服务器&#xff0c;更是引发了广大用户的关注。本文将详细解析这款云服务器的特点、优势以及适用场景&#xff0c;为大家上云提供参考。 一…

Android逆向学习(六)绕过app签名校验,通过frida,io重定向(上)

Android逆向学习&#xff08;六&#xff09;绕过app签名校验&#xff0c;通过frida&#xff0c;io重定向&#xff08;上&#xff09; 一、写在前面 这是吾爱破解正己大大教程的第五个作业&#xff0c;然后我的系统还是ubuntu&#xff0c;建议先看一下上一个博客&#xff0c;关…

阿赵UE学习笔记——8、贴图导入设置

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的用法&#xff0c;这次来说一下贴图的导入设置。   在内容浏览器里面可以看到纹理类型的资源&#xff0c;就是贴图了&#xff0c;鼠标悬浮在上面可以看到这个纹理贴图的信息&#xff1a; 双击纹理贴图…

使用Scikit Learn 进行识别手写数字

使用Scikit Learn 进行识别手写数字 作者&#xff1a;i阿极 作者简介&#xff1a;数据分析领域优质创作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&#x1f…

虽迟但到!MySQL 可以用 JavaScript 写存储过程了!

任何能用 JavaScript 来干的事情&#xff0c;最终都会用 JavaScript 来干 背景 不久前&#xff0c;Oracle 在 MySQL 官方博客官宣了在 MySQL 中支持用 JavaScript 来写存储过程。 最流行的编程语言 最流行的数据库。程序员不做选择&#xff0c;当然是全都要。 使用方法 用 J…

压测必经之路,Jmeter分布式压测教程

01、分布式压测原理 Jemter分布式压测是选择其中一台作为调度机&#xff08;master&#xff09;&#xff0c;其他机器作为执行机&#xff08;slave&#xff09;&#xff1b;当然一台机器也可以既做调度机&#xff0c;也做执行机。 调度机执行脚本的时候&#xff0c;master将会…

软件测试工具Robot Framework如何安装

安装文件准备 表1 安装文件准备 Robot框架结构 为了更好的了解环境安装&#xff0c;我们先看下框架结构&#xff1a; 图1 Robot Framework Architecture Robot Framework 通过导入不同的库&#xff0c;就可以使用库中所提供的关键字&#xff0c;从而时行相关的测试。有几个标…

掌握Java Future模式及其灵活应用

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;今天咱们来聊聊Future。咱们程序员在日常工作中&#xff0c;肯定都遇到过需要处理耗时任务的情况&#xff0c;特别是在Java领域。比如说&#xff0c;小黑要从网络上下载数据&#xff0c;或者要执行一个计算密集型…

MySql -数据库基本概念

一、数据库的基本概念 1.为什么要学数据库&#xff1f; 之前我们如果想将一些数据实现永久化存储&#xff0c;可以怎么做呢&#xff1f;没错。使用IO流的技术将数据保存到本地文件中但是接下来我有这样一个需求&#xff1a;将下面的user.txt文件中的王五年龄修改为35 张三 2…

Java顺序表(1)

&#x1f435;本篇文章将对顺序表中的方法进行模拟实现 一、线性表 线性表是指在逻辑结构上呈连续的线性结构&#xff0c;而在物理结构上不一定是连续的结构&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列等 二、顺序表 顺序表一般采用数组来存储数据&#x…

C++——map和set的基本使用

目录 一&#xff0c;关联式容器 二&#xff0c;键值对 三&#xff0c;set的使用 3.1 set介绍 3.2 set的插入和删除 3.3 set的pair 3.4 multiset 四&#xff0c;map的使用 4.1 map介绍 4.2 map实现简易字典 4.3 map实现统计次数 4.4 map的[] 五&#xff0c;使用map或…

CHS_01.2.1.1+2.1.3+进程的概念、组成、特征

CHS_01.2.1.12.1.3进程的概念、组成、特征 进程进程的概念 进程的组成——PCB进程的组成——PCB进程的组成——程序段、数据段知识滚雪球&#xff1a;程序是如何运行的&#xff1f;进程的组成进程的特征 知识回顾与重要考点 从这个小节开始 我们会正式进入第二章处理机管理相关…

暴雨信息发布算力网络应用平台打造零感知算网服务新模式

为进一步优化算力网络应用服务能力和降低算力网络使用难度&#xff0c;暴雨信息突破基于算力网络的实例跨域协同与迁移、基于测试评估的应用度量和解构等技术&#xff0c;研发并推出算力网络应用平台。该系统通过提供一种即开即用、按需付费的零感知算网应用服务&#xff0c;使…

捕捉小红书开年顶流,品牌快“跟风”上车!

2024年开年&#xff0c;“南方小土豆”纷纷北上&#xff0c;元旦期间哈尔滨收入近60亿元&#xff0c;“冰雪”一跃成为今冬最炙手可热的流行趋势&#xff01; 千瓜数据显示&#xff0c;小红书“冰雪”相关商业笔记同比去年增长649.84%。本期&#xff0c;千瓜将从“冰雪”场景出…

离散数学-二元关系

4.1关系的概念 1)序偶及n元有序组 由两个个体x和y&#xff0c;按照一定顺序排序成的、有序数组称为有序偶或有序对、二元有序组&#xff0c; 记作<x&#xff0c;y>&#xff0c;其中x是第一分量&#xff0c;y是第二分量。 相等有序偶&#xff1a;第一分量和第二分量分…

Excel·VBA按指定顺序排序函数

与之前写过的《ExcelVBA数组冒泡排序函数》不同&#xff0c;不是按照数值大小的升序/降序对数组进行排序&#xff0c;而是按照指定数组的顺序&#xff0c;对另一个数组进行排序 以下代码调用了《ExcelVBA数组冒泡排序函数》bubble_sort_arr函数&#xff08;如需使用代码需复制…

EasyPOI导出报表

报表导出是一种很常见的功能&#xff0c;只要是开发都会涉及到这一功能&#xff0c;早些年经常集成poi完成导出功能&#xff0c;我之前也有写过关于poi导出的文章&#xff0c;现如今&#xff0c;也有了更为方便的导出插件 — EasyPOI&#xff0c;废话不多说&#xff0c;开始撸代…