信号和槽是两种函数,这是Qt在C++基础上新增的特性,类似于其他技术中的回调的概念。
信号和槽通过程序员提前设定的“约定”,可以实现对象之间的通信,有两个先决的条件:
信号槽需要在使用前进行“约定”,这个约定被称为连接。
【例子】:如果金龙考试考了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]
为了学习,把信号槽分为三种实现方式。
这种连接方式是最简单的,因为信号函数和槽函数都是Qt内置的,只需要在文档中查询出函数后,使用connect函数连接即可。
查找函数:在对应的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;
}
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;
}
为了讲解,强行使用自定义信号,并非问题的最优解,主要学习写法。
信号函数是一个非常特殊的函数,因此只有声明,没有定义,没有函数体。因此无法调用,只能使用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连接多次)
【例子】点击按钮,按钮上显式点击的次数。
//?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);
}
需要注意的是:
一对多指的是一个信号连接多个槽函数。
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();
}
在头文件内声明的函数,可以以下操作在主函数内自定添加定义
多对一指的是多个信号连接同一个槽函数。多对一的问题在于槽函数无法直接判断那个信号触发的槽函数调用,可以通过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()?<<?"对象错误"?;
}
}