C++qt-信号-信号槽

发布时间:2024年01月09日

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()?<<?"对象错误"?;
    }
}

文章来源:https://blog.csdn.net/m0_74937538/article/details/135484006
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。