目录
1. 概念
信号和槽是两种函数,这是Qt在C++的基础上新增的特性,类似于其他技术中的回调的概念。
可以理解为信号槽机制就是:“如果A对象......,则B对象...... ”
信号槽通过程序员提前设定的“约定”,可以实现对象之间的通信,有两个先决条件:
● 通信的对象必须都是从QObject类中派生出来的
● 类中要有Q_OBJECT宏
2. 函数原型
信号槽需要在使用前进行“约定”,这个“约定”被称为连接,使用下面的函数实现:
【例子】翰林考100分,老孟请吃饭
// 参数1 发射者,表示起因发起的对象,通常是一个名词(翰林)
// 参数2 信号函数,表示起因发起的动作,通常是一个动词(考100分)需要用SIGNAL()包裹
// 参数3 接受者,表示结果发起的对象,通常是一个名词(老孟)
// 参数4 槽函数,表示结果发起的动作,通常是一个动词(请吃饭)需要用SLOT()包裹
QObject::connect(const QObject * sender,
const char * signal,
const QObject * receiver,
const char * method)
3. 实现
为了学习,把信号槽分为三种实现的方式:
● 自带信号→自带槽
● 自带信号→自定义槽
● 自定义信号
3.1 自带信号→自带槽
这种连接方式是最简单的,因为信号函数和槽函数都是Qt内置的,只需要查询出函数后,使用connect函数连接即可。
【例子】点击按钮,关闭窗口
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;
};
#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);
// 【连接信号槽】点击按钮,关闭窗口
// 参数1 按钮对象
// 参数2 信号函数clicked
// 参数3 窗口对象
// 参数4 关闭槽函数close
connect(btn,SIGNAL(clicked()),
this,SLOT(close()));
}
Dialog::~Dialog()
{
delete btn;
}
3.2 自带信号→自定义槽
Qt不可能内置所有执行的动作代码,特别是一些复杂的动作,需要开发者手动编写槽函数。这种方式是所有连接方式中使用的最多的。
槽函数实际上是一种特殊的成员函数,在声明的时候权限的作用主要是修饰其作为普通成员函数使用的效果,不影响信号槽的连接效果。
【例子】点击按钮,窗口向下移动10个像素,向右移动10个像素
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();
private:
QPushButton *btn;
// 声明自定义槽函数
private slots:// 最小权限法则
void mySlot(); // 小驼峰命名法:第一个单词全小写,其他单词的首字母大写
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(300,300);
btn = new QPushButton("移动",this);
btn->move(100,100);
connect(btn,SIGNAL(clicked()),
this,SLOT(mySlot()));
}
Dialog::~Dialog()
{
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关键字发射。
【例子】点击按钮,关闭窗口
3.1的信号槽连接方式
本节强行在中间增加一层自定义信号转发的过程:
信号槽连接
槽函数的内部实现
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();
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(300,300);
btn = new QPushButton("关闭窗口",this);
btn->move(100,100);
// 第一个信号槽
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
// 第二个信号槽
connect(this,SIGNAL(mySignal()),this,SLOT(close()));
}
Dialog::~Dialog()
{
delete btn;
}
void Dialog::mySlot()
{
// 发射信号mySignal()
emit mySignal();
qDebug() << "发射信号";
}
4. 信号槽传参
【例子】点击按钮,按钮显示点击的次数
QPushButton 的文字属性为text : QString ,可以使用setter更改按钮文字
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();
private:
QPushButton *btn;
int count;
private slots:
void mySlot();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(300,300);
btn = new QPushButton("0",this);
btn->move(100,100);
// count = 0;
// 连接信号槽
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
}
Dialog::~Dialog()
{
delete btn;
}
void Dialog::mySlot()
{
// 静态局部变量
static int count = 0;
count++;
// 转换类型int → QString
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();
private:
QPushButton *btn;
private slots:
void mySlot(); // 发射自定义信号
void mySlot2(int); // 与mySignal(int)链接
signals:
void mySignal(int); //带参数的信号函数
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(400,400);
btn = new QPushButton("0",this);
btn->move(150,200);
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)
{
QString text = QString::number(count);
// 更改文字
btn->setText(text);
}
需要注意的是:
● 理论上可以传递任意多个参数,但是实际上1-2个居多
● 信号的参数个数必须大于等于槽的参数个数
● 信号的参数类型要与槽的参数类型匹配
5.对应关系
5.1 一对多
一对多指的是一个信号可以连接到多个槽函数,对于一对多的连接关系,可以合并为一对一,因为槽函数也是一个成员函数,可以整合到一个槽函数中进行连接
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();
private:
QPushButton *btn1;
QPushButton *btn2;
private slots:
void mySlot1();
void mySlot2();
void mySlot13(); // 一对一的槽函数
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(400,400);
btn1 = new QPushButton("一对多",this);
btn1->move(100,100);
btn2 = new QPushButton("一对一",this);
btn2->move(200,200);
// 一对多
connect(btn1,SIGNAL(clicked()),
this,SLOT(mySlot1()));
connect(btn1,SIGNAL(clicked()),
this,SLOT(mySlot2()));
// 一对多的优势是可以灵活处理每个对应关系
// 例如可以断开某个信号槽的连接
// 断开连接的函数与连接函数一样,只需要函数名称前加一个dis
disconnect(btn1,SIGNAL(clicked()),
this,SLOT(mySlot2()));
// 一对一
connect(btn2,SIGNAL(clicked()),
this,SLOT(mySlot13()));
}
Dialog::~Dialog()
{
delete btn1;
delete btn2;
}
void Dialog::mySlot1()
{
qDebug() << "A";
}
void Dialog::mySlot2()
{
qDebug() << "B";
}
void Dialog::mySlot13()
{
// 直接调用槽函数
mySlot1();
mySlot2();
}
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();
private:
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(btn1 == sender())
{
qDebug() << "A";
}else if(btn2 == sender())
{
qDebug() << "B";
}
}
Tips: