目录
一、信号槽的概念
类似于一些其它的编程语言或框架中的回调机制,信号槽是Qt基于C++语法新增的特性,使用起来非常方便,可以完成不同对象之间的通信。
信号槽的使用需要有以下两个条件:
1. 通信的对象必须继承自QObject类,QObject是Qt所有内置类型的基类。
2. 类中要有Q_OBJECT宏
二、函数原型
信号槽需要连接后才能触发,因此信号槽的核心是连接函数,连接函数是一个静态成员函数。
参数1:发射者,发射者是一个对象,此对象发射信号函数,作为信号槽的触发条件。
参数2:信号函数,参数1中的发射者发射出的信号函数,作为信号槽的触发条件。需要使用 SIGNAL()包裹函数名称
参数3:接收者,接收者是一个对象,此对象执行槽函数,作为信号槽的执行结果。
参数4:槽函数,参数3中接收者要执行的槽函数,作为信号槽的执行结果。需要使用SLOT() 包裹函数名称。
接收者绑定了发射者的信号函数,一旦发射者发射信号函数,接收者就执行槽函数。
三、连接方式
为了方便讲解,依次使用三种方式进行信号槽的连接。
- 自带信号 → 自带槽
- 自带信号 → 自定义槽
- 自定义信号 → 槽
3.1 自带信号 → 自带槽
这种方式是最简单的一种连接方式,因为信号函数和槽函数Qt都内置了,只需要程序员从文档中找到对应函数连接即可。
【例子】点击按钮,关闭窗口。
分析:先找因果关系的四个参数。
参数1:发射者,按钮对象
参数2:信号函数,点击
参数3:接收者,窗口对象
参数4:槽函数,关闭
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;//窗口中的子组件必须放在堆内存里面(就是必须用指针),
而且必须在.h里面定义成全局的 因为还要析构不然调不了
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(400,400);
btn = new QPushButton("关闭",this);
btn->setGeometry(100,100,100,100);
// 连接信号槽
// 参数1:发射者,按钮对象
// 参数2:信号函数,点击
// 参数3:接收者,窗口对象
// 参数4:槽函数,关闭
connect(btn,SIGNAL(clicked()),this,SLOT(close()));
//点击按钮 发射信号 由于窗口绑定了信号函数 所以窗口接收信号并触发了槽函数
}
Dialog::~Dialog()
{
delete btn;
}
3.2 自带信号 → 自定义槽
在实际的开发中,Qt不可能内置所有的函数,特别是槽函数,更多的情况是需要用户自定义一个槽函数来实现特定的功能,这种方式也是最常见的一种信号槽连接方式。
需要注意的是,槽函数是一种特殊的成员函数,实现槽函数的方式与普通成员函数类似。
注意:一定要先链接再触发信号 不然你虽然触发了信号但是接收者没有进行链接就触发不了槽函数
【例子】点击按钮,向右下方移动窗口并输出当前的窗口坐标。分析:
参数1:按钮对象
参数2:clicked
参数3:窗口对象
参数4:自定义槽函数
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(400,400);
btn = new QPushButton("向右下方移动",this);
btn->setGeometry(100,100,100,100);
// 连接信号槽
// 参数1:按钮对象 btn
// 参数2:clicked
// 参数3:窗口对象 this
// 参数4:自定义槽函数 mySlot
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));//类内通过this指针找到w,
第三个参数为什么是this因为自定义信号槽函数属于Dialog类而这个类只有一个对象就是w,
类内想要调w就必须用this
}
//法一:手写 法二:再定义中鼠标中点击重构
void Dialog::mySlot()//类内声明类外定义必须加作用域限定符
{
// 先获得当前窗口的坐标
int x = this->x();
int y = this->y();
// 增加后移动
move(x+10,y+10);
// 输出当前坐标
qDebug() << x+10 << "," << y+10;
}
Dialog::~Dialog()
{
delete btn;
}
3.3 自定义信号 → 槽
这种做法主要用于后期一些稍微复杂的场景,现在讲解无法使用合适案例演示,因此讲解的过程中并不是最优解,属于强行使用。
【例子】点击按钮,关闭窗口。
之前的思路:
现在的思路:
现在的思路需要有两个信号槽的连接,在自定义槽函数中要手动发射一个自定义信号函数,信号函数是一种非常特殊的函数,信号函数只有声明,没有定义;信号函数没有权限。
首先按钮与窗口进行链接connect 再点击按钮 按钮发射一个信号 由于两者绑定所以窗口会收到这个信号并触发槽函数,然后再去链接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;
private slots:
// 自定义槽函数
void mySlot();
signals: // 信号函数没有权限
// 自定义信号函数只需要声明就能使用
void mySignal();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
btn = new QPushButton("关闭",this);
btn->move(100,100);
// 第一个信号槽的连接
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
// 第二个信号槽的连接
connect(this,SIGNAL(mySignal()),this,SLOT(close()));
//点击按钮发射点击信号给窗口 窗口收到信号并触发槽函数 ,
槽函数里面 又触发信号,由窗口发送给窗口 窗口收到信号触发槽函数 执行函数操作关闭窗口
}
void Dialog::mySlot()
{
// 发射自定义信号
emit mySignal();
}
Dialog::~Dialog()
{
delete btn;
}
四、参数传递
4.1 全局参数
在一个类中进行参数传递,可以通过成员变量等方式。
【例子】点击按钮,按钮上显示当前按钮的点击次数。
QPushButton的text : QString属性表示显示的文字,对应的getter和setter为:
QString text() const 读取内容
void setText(const QString & text) 设置内容
setter函数不是槽函数,因此需要自定义槽函数,在自定义槽函数中调用text属性的setter函数改变其数值。
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:
int count; // 点击按钮的次数
QPushButton* btn;
private slots:
void setTextSlot(); // 自定义槽函数
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent),count(0)
{
btn = new QPushButton("0",this);
btn->move(100,100);
// 点击按钮,设置当前点击的次数
// 连接信号槽
connect(btn,SIGNAL(clicked()),this,SLOT(setTextSlot()));
}
void Dialog::setTextSlot()
{
// 点击次数+1
count++;
// int → QString
QString text = QString::number(count);//类型转换函数
// 设置当前点次数的字符串 设置到按钮上显示
btn->setText(text);
}
Dialog::~Dialog()
{
delete btn;
}
4.2 信号槽传参
这种做法主要用于后期一些稍微复杂的场景,现在讲解无法使用合适案例演示,因此讲解的过程中并不是最优解,属于强行使用。
【例子】点击按钮,按钮上显示当前按钮的点击次数。
4.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:
int count; // 点击按钮的次数
QPushButton* btn;
private slots:
void mySlot1(); // 自定义槽函数1
void mySlot2(QString); // 自定义槽函数2
signals:
// 带参数的自定义信号,参数使用哑元
void mySignal(QString);
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent),count(0)
{
btn = new QPushButton("0",this);
btn->move(100,100);
// 点击按钮,设置当前点击的次数
// 连接信号槽1
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot1()));
// 连接信号槽2
connect(this,SIGNAL(mySignal(QString)),this,SLOT(mySlot2(QString)));
}
void Dialog::mySlot1()
{
// 点击次数+1
count++;
// int → QString
QString text = QString::number(count);
// 发射带参数的自定义信号
emit mySignal(text);
}
/**
* @brief Dialog::mySlot2
* @param text 信号发来的参数
*/
void Dialog::mySlot2(QString text)
{
// 更新按钮显示
btn->setText(text);
}
Dialog::~Dialog()
{
delete btn;
}
信号槽传参需要注意以下几点:
- 理论上可以传递任意个数的参数,但是实际开发建议1-2个参数
- 信号的参数个数必须大于等于槽的参数个数
- 参数类型必须一一匹配
五、信号槽的对应关系
同一个信号可以连接到多个槽(一对多),多个信号也可以连接到同一个槽(多对一)。一对多的情况可以优化为一对一。
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 mySlot3();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
btn1 = new QPushButton("1",this);
btn2 = new QPushButton("2",this);
btn1->move(50,50);
btn2->move(100,100);
// 点击btn1,一对多
connect(btn1,SIGNAL(clicked()),this,SLOT(mySlot1()));
connect(btn1,SIGNAL(clicked()),this,SLOT(mySlot2()));
// 可以优化为btn2的一对一
connect(btn2,SIGNAL(clicked()),this,SLOT(mySlot3()));
}
void Dialog::mySlot1()
{
qDebug() << "槽函数1";
}
void Dialog::mySlot2()
{
qDebug() << "槽函数2";
}
void Dialog::mySlot3()
{
// 槽函数也是成员函数,可以直接调用
mySlot1();
mySlot2();
}
Dialog::~Dialog()
{
delete btn1;
delete btn2;
}