C++qt-信号-信号槽

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_OBJECTpublic: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_OBJECTpublic:
    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.hqwc.cn/news/334246.html

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

相关文章

开关电源损耗测量之高压空载测试方法 -纳米软件

开关电源在实际使用过程中会产生损耗&#xff0c;损耗过大会直接影响到开关电源的转换效率和性能&#xff0c;从而导致设备无法正常运行。因此测试开关电源的损耗情况也是非常重要的步骤。 高压空载运行测试用来检测开关电源的损耗情况&#xff0c;在空载情况下&#xff0c;带软…

强化学习的数学原理学习笔记 - 策略梯度(Policy Gradient)

文章目录 概览&#xff1a;RL方法分类策略梯度&#xff08;Policy Gradient&#xff09;Basic Policy Gradient目标函数1&#xff1a;平均状态值目标函数2&#xff1a;平均单步奖励&#x1f7e1;PG梯度计算 &#x1f7e6;REINFORCE 本系列文章介绍强化学习基础知识与经典算法原…

正则表达式Regex

是什么&#xff1a;一句话&#xff0c;正则表达式是对字符串执行模式匹配的技术。 从一段字符串中提取出所有英文单词、数字、字母和数字。 如果采用传统方法&#xff1a;将字符串的所有字符分割成单个&#xff0c;根据ASCII码判断&#xff0c;在一定范围内就是字母&#xff…

Fiddler工具 — 10.Statistics(统计)面板

1、Statistics介绍 Statistics 页签显示当前用户选择的 Sessions 的汇总信息&#xff0c;包括&#xff1a;选择的 Sessions 总数、发送字节数、接收字节数、响应类型的汇总表、世界各地通过不同请求方式所需的时间等。 Statistics 分页还会统计请求和响应的其他一些信息,如&a…

QT qss文件设置样式

方式一 &#xff08;单个&#xff09; 方式二 &#xff08;全局&#xff09; 所有按钮都会采用这个样式。 方式三 &#xff08;qss文件&#xff09; 创建资源文件 创建qss文件&#xff08;Button.qss&#xff09; 引用qss文件 QApplication a(argc, argv);QString qss;QFile…

【漏洞复现】锐捷RG-UAC统一上网行为管理系统信息泄露漏洞

Nx01 产品简介 锐捷网络成立于2000年1月&#xff0c;原名实达网络&#xff0c;2003年更名&#xff0c;自成立以来&#xff0c;一直扎根行业&#xff0c;深入场景进行解决方案设计和创新&#xff0c;并利用云计算、SDN、移动互联、大数据、物联网、AI等新技术为各行业用户提供场…

Selenium自动化程序被检测为爬虫,怎么屏蔽和绕过

Selenium 操作被屏蔽 使用selenium自动化网页时&#xff0c;有一定的概率会被目标网站识别&#xff0c;一旦被检测到&#xff0c;目标网站会拦截该客户端做出的网页操作。 比如淘宝和大众点评的登录页&#xff0c;当手工打开浏览器&#xff0c;输入用户名和密码时&#xff0c…

Java后端开发——SSM整合实验

文章目录 Java后端开发——SSM整合实验一、常用方式整合SSM框架二、纯注解方式整合SSM框架 Java后端开发——SSM整合实验 一、常用方式整合SSM框架 1.搭建数据库环境&#xff1a;MySQL数据库中创建一个名称为ssm的数据库&#xff0c;在该数据库中创建一个名称为tb_book的表 …

2024年美国大学生数学建模思路 - 复盘:人力资源安排的最优化模型

文章目录 0 赛题思路1 描述2 问题概括3 建模过程3.1 边界说明3.2 符号约定3.3 分析3.4 模型建立3.5 模型求解 4 模型评价与推广5 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 描述 …

【算法系列 | 12】深入解析查找算法之—斐波那契查找

序言 心若有阳光&#xff0c;你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏&#xff0c;希望能帮助大家很好的了解算法。主要深入解析每个算法&#xff0c;从概念到示例。 我们一起努力&#xff0c;成为更好的自己&#xff01; 今天第12讲&#xff0c;讲…

在wsl中安装miniconda

下载安装包 打卡miniconda的官网https://docs.conda.io/projects/miniconda/en/latest/,下载下来安装包&#xff0c;或者直接在乌班图中运行命令wget https://repo.anaconda.com/miniconda/Miniconda3-py38_23.5.2-0-Linux-x86_64.sh,等待下载完毕 安装 到下载目录下执行命令…

vue中鼠标拖动触发滚动条的移动

前言 在做后端管理系统中&#xff0c;像弹窗或大的表单时&#xff0c;经常会有滚动条的出现&#xff0c;但有些时候如流程、图片等操作时&#xff0c;仅仅使用鼠标拖动滚动条操作不太方便&#xff0c;如果使用鼠标拖拽图片或容器来触发滚动条的移动就比较方便了 功能设计 如…