小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
一、信号和槽
1.1 相关概念
==信号槽==是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
我们主要研究的是四个东西:
- 信号的发起者
- 信号
- 信号接收者
- 槽函数
比如:按钮按下后让当前窗口关闭:
- 信号发送者:按钮
- 信号:按钮被按下
- 信号接收者:窗口
- 槽函数:关闭窗口
接收者接收到信号的处理方式函数:connect
connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method)
功能:接收者接收到指定信号的处理操作
参数:
sender:信号的发起者
signal:指定的信号,根据实际情况来确定信号
信号可以使用系统的,也可以自定义
receiver:信号接收方
method:槽函数,根据实际情况来确定槽函数
槽函数可以使用系统的,也可以自定义
1.2 系统自带信号和槽函数
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//**************设置窗口属性***************
this->setWindowTitle("按钮操作");
this->setFixedSize(800, 600);
//**************创建按钮并设置属性************
QPushButton *buttonReg = new QPushButton("注册", this);
QPushButton *buttonLog = new QPushButton("登录", this);
QPushButton *buttonQuit = new QPushButton("退出", this);
buttonReg->setFixedSize(100, 50);
buttonLog->setFixedSize(100, 50);
buttonQuit->setFixedSize(100, 50);
buttonReg->move(150, 200);
buttonLog->move(300, 200);
buttonQuit->move(450, 200);
//**************根据按钮的信号设置处理方式*****************
//Qt5.0以上版本使用一下形式
connect(buttonQuit, &QPushButton::clicked, this, &QWidget::close);
//Qt4.x版本只能使用一下形式
//connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close()));
}
Widget::~Widget()
{
}
1.3 用户自定义槽函数
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//一般成员函数作为槽函数需要用slots声明一下
public slots:
void buttonLogSlot();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QPushButton>
#include <QDebug>
void buttonRegSlot()
{
qDebug()<< "注册按钮点击了";
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//**************设置窗口属性***************
this->setWindowTitle("按钮操作");
this->setFixedSize(800, 600);
//**************创建按钮并设置属性************
QPushButton *buttonReg = new QPushButton("注册", this);
QPushButton *buttonLog = new QPushButton("登录", this);
QPushButton *buttonQuit = new QPushButton("退出", this);
QPushButton *buttonCancel = new QPushButton("取消", this);
buttonReg->setFixedSize(100, 50);
buttonLog->setFixedSize(100, 50);
buttonQuit->setFixedSize(100, 50);
buttonCancel->setFixedSize(100, 50);
buttonReg->move(150, 200);
buttonLog->move(300, 200);
buttonQuit->move(450, 200);
buttonCancel->move(150, 300);
//**************根据按钮的信号设置处理方式*****************
//---系统自带信号和槽函数---
//点击退出按钮让窗口关闭
//Qt5.0以上版本使用一下形式
connect(buttonQuit, &QPushButton::clicked, this, &QWidget::close);
//Qt4.x版本只能使用一下形式
//connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close()));
//---自定义槽函数---
//1、使用全局函数定义槽函数
connect(buttonReg, &QPushButton::clicked, this, buttonRegSlot);
//2、使用成员函数定义槽函数
connect(buttonLog, &QPushButton::clicked, this, buttonLogSlot);
//3、使用lambda表达式实现槽函数
connect(buttonCancel, &QPushButton::clicked, this, [=](){
qDebug()<< "取消按钮点击了";
});
}
Widget::~Widget()
{
}
void Widget::buttonLogSlot()
{
qDebug()<< "登录按钮点击了";
}
1.4 实现窗口切换
1.4.1 添加新的窗口
在当前工程的名字位置鼠标右键选择“Add New”,在弹出的窗口中选择“C++”中的“C++ Class”
设置类名,并设置父类
一路下一步点击完成即可
1.4.2 实现页面之间切换并传值
newwindow.h
#ifndef NEWWINDOW_H
#define NEWWINDOW_H
#include <QWidget>
class Newwindow : public QWidget
{
Q_OBJECT
public:
explicit Newwindow(QWidget *parent = nullptr);
//自定义信号,返回值为void,
//不需要自己实现,参数可以有,也可以没有
signals:
void mysignal();
void mysignal1(int val);
};
#endif // NEWWINDOW_H
newwindow.cpp
#include "newwindow.h"
#include <QPushButton>
Newwindow::Newwindow(QWidget *parent) : QWidget(parent)
{
this->setWindowTitle("这是另一个窗口");
this->setFixedSize(600, 300);
QPushButton *button = new QPushButton("上一页", this);
button->setFixedSize(100, 50);
button->move(50, 50);
connect(button, &QPushButton::clicked, [=](){
//发信号,当前窗口发送的信号
emit this->mysignal();
});
QPushButton *button1 = new QPushButton("FRONT", this);
button1->setFixedSize(100, 50);
button1->move(150, 50);
connect(button1, &QPushButton::clicked, [=](){
//发信号的同时传值
emit this->mysignal1(888);
});
}
widget.h
ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//一般成员函数作为槽函数需要用slots声明一下
public slots:
void buttonLogSlot();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QPushButton>
#include <QDebug>
#include "newwindow.h"
void buttonRegSlot()
{
qDebug()<< "注册按钮点击了";
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//**************设置窗口属性***************
this->setWindowTitle("按钮操作");
this->setFixedSize(800, 600);
//**************创建按钮并设置属性************
QPushButton *buttonReg = new QPushButton("注册", this);
QPushButton *buttonLog = new QPushButton("登录", this);
QPushButton *buttonQuit = new QPushButton("退出", this);
QPushButton *buttonCancel = new QPushButton("取消", this);
QPushButton *buttonNext = new QPushButton("下一页", this);
buttonReg->setFixedSize(100, 50);
buttonLog->setFixedSize(100, 50);
buttonQuit->setFixedSize(100, 50);
buttonCancel->setFixedSize(100, 50);
buttonNext->setFixedSize(100, 50);
buttonReg->move(150, 200);
buttonLog->move(300, 200);
buttonQuit->move(450, 200);
buttonCancel->move(150, 300);
buttonNext->move(300, 300);
//**************根据按钮的信号设置处理方式*****************
//---系统自带信号和槽函数---
//点击退出按钮让窗口关闭
//Qt5.0以上版本使用一下形式
connect(buttonQuit, &QPushButton::clicked, this, &QWidget::close);
//Qt4.x版本只能使用一下形式
//connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close()));
//---自定义槽函数---
//1、使用全局函数定义槽函数
connect(buttonReg, &QPushButton::clicked, this, buttonRegSlot);
//2、使用成员函数定义槽函数
connect(buttonLog, &QPushButton::clicked, this, buttonLogSlot);
//3、使用lambda表达式实现槽函数
connect(buttonCancel, &QPushButton::clicked, this, [=](){
qDebug()<< "取消按钮点击了";
});
//页面切换
//创建新窗口
Newwindow *newwindow = new Newwindow;
//使用connect函数切换窗口
connect(buttonNext, &QPushButton::clicked, [=](){
//隐藏当前窗口
this->hide();
//开启新的窗口
newwindow->show();
});
//接收新窗口发送的信号,隐藏新窗口,开辟原窗口
connect(newwindow, &Newwindow::mysignal, this, [=](){
newwindow->hide();
this->show();
});
//************************************************
//页面切换之间可以通过信号和槽实现数据的传递
connect(newwindow, &Newwindow::mysignal1, [=](int val){
newwindow->hide();
this->show();
qDebug()<< val;
});
}
Widget::~Widget()
{
}
void Widget::buttonLogSlot()
{
qDebug()<< "登录按钮点击了";
}
1.5 信号与槽总结
自定义信号槽需要注意的事项:
- 发送者和接收者都需要是
QObject的子类(当然,槽函数是全局函数Lambda 表达式等无需接收者的时候除外); - 信号和槽函数返回值是
void - 信号只需要声明,不需要实现
- 槽函数需要声明,也需要实现
- 槽函数是普通的成员函数,作为成员函数,会受到
public、private、protected的影响; - 使用
emit在恰当的位置发送信号; - 使用
connect()函数连接信号和槽。 - 任何成员函数、
static函数、全局函数和Lambda表达式都可以作为槽函数 - ==信号槽要求信号和槽的参数一致==,所谓一致,是参数类型一致。如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
信号槽的拓展:
- 一个信号可以和多个槽相连 如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
- 多个信号可以连接到一个槽 只要任意一个信号发出,这个槽就会被调用。
- 一个信号可以连接到另外的一个信号 当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
- 槽可以被取消链接
这种情况并不经常出现,因为当一个对象
delete之后,Qt自动取消所有连接到这个对象上面的槽。 - 信号槽可以断开
利用
disconnect关键字是可以断开信号槽的 - 使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持
Lambda表达式的。 在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。