目录
1. connect函数
点击按钮,关闭Widget
connect(btn, &QPushButton::clicked, this, &Widget::close);
四个参数:
- 参数1:信号发送者
- 参数2:发送的信号signals(函数地址)
- 参数3:信号接收者
- 参数4:处理的slot槽函数(函数指针)
Widget::Widget(QWidget *parent)
: QWidget(parent) // 用于初始化Widget类的基类,即QWidget。
{
// 创建一个按钮
QPushButton *btn = new QPushButton;
btn->setParent(this);
btn->setText(QString::fromLocal8Bit("按钮1"));
// connect(信号发送者,发送的具体信号,信号接收者,信号的处理)
connect(btn, &QPushButton::clicked, this, &QWidget::close);
}
2. 自定义的信号和槽
信号:
- 自定义信号,写在signals下。
- 返回值是void,只需要声明,不需要实现
- 可以有参数,所以可以重载
槽:
- slots. 早期qt必须写在public slots下.
- 返回值void,需要声明和实现
- 可以有参数,所以可以重载
举个例子:下课了,触发老师发送饿了的信号,学生接受信号并进行请客吃饭事件。
程序:调用classIsOver(),触发老师hungry(), 学生接受信号并treat();
Teacher类:
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
void hungry(); // 信号
};
#endif // TEACHER_H
#include "teacher.h"
Teacher::Teacher(QObject *parent)
: QObject{parent}
{}
Student类:
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
void treat(); // 槽函数
signals:
};
#endif // STUDENT_H
触发函数,即下课了。
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "teacher.h"
#include "student.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
// Ui::Widget *ui;
Teacher *t;
Student *s;
void classIsOver(); // 触发函数
};
#endif // WIDGET_H
#include "widget.h"
// #include "ui_widget.h"
// 下课后,老师饿了,学生请客吃饭。
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// 信号发送者
this->t = new Teacher();
// 信号接收者
this->s = new Student();
// 绑定老师饿了,学生就请客吃饭
connect(t, &Teacher::hungry, s, &Student::treat);
// 调用触发函数
classIsOver();
}
Widget::~Widget()
{
}
// 触发函数
void Widget::classIsOver()
{
// 下课了,调用此触发函数classIsOver,触发老师饿了信号
emit t->hungry();
}
3. 带参数的信号和槽
预备知识点:
(1)普通函数指针:
int (*fp)(int a); // 这里就定义了一个指向函数(这个函数参数仅仅为一个 int 类型,函数返回值是 int 类型)的指针 fp。
(2)类函数指针:
int (Teacher:: *fp)(int a); // 指向的是Teacher类中函数,该函数参数是int类型,返回值是int,指针变量是fp;
void (Teacher:: *teacherSignal)(QString foodName) = &Teacher::hungry; // 指向的Teacher类中的hungry函数,该函数参数是QString类型,返回void类型。
同样用上面的吃饭例子:
下课了,触发老师饿了,老师吃烧鸡,学生接收吃烧鸡信号并请客。
// 触发函数
void Widget::classIsOver()
{
// 下课了,调用此触发函数,触发老师饿了信号
// emit t->hungry();
emit t->hungry("烧鸡"); // Teacher *t;
}
Teacher类型,加一个带参声明。
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
// 自定义信号,写在signals下。
// 返回值是void,只需要声明,不需要实现
// 可以有参数,所以可以重载
void hungry();
void hungry(QString foodName);
};
#endif // TEACHER_H
Student类加一个带参函数
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
// slots. 早期qt必须写在public slots下.
// 返回值void,需要声明和实现
// 可以有参数,所以可以重载
void treat();
void treat(QString foodName);
signals:
};
#endif // STUDENT_H
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent)
: QObject{parent}
{}
void Student::treat()
{
qDebug() << "请老师吃饭!";
}
void Student::treat(QString foodName)
{
qDebug() << "eat: " << foodName; // eat: "烧鸡"
qDebug() << "eat: " << foodName.toUtf8().data(); // eat: 烧鸡
}
定义函数指针,关联信号和槽
#include "widget.h"
#include <QPushButton>
// 下课后,老师饿了,学生请客吃饭。
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// 信号发送者
this->t = new Teacher();
// 信号接收者
this->s = new Student();
// 绑定老师饿了,学生就请客吃饭
// 不带参
// connect(t, &Teacher::hungry, s, &Student::treat);
// 带参
void (Teacher:: *teacherSignal)(QString foodName) = &Teacher::hungry;
void (Student:: *studentSlot)(QString foodName) = &Student::treat;
connect(t, teacherSignal, s, studentSlot);
// 直接调用触发函数
classIsOver();
// 通过按钮,调用触发函数。
QPushButton *btn = new QPushButton("下课", this);
this->resize(600, 400);
connect(btn, &QPushButton::clicked, this, &Widget::classIsOver);
}
Widget::~Widget()
{
}
// 触发函数
void Widget::classIsOver()
{
// 下课了,调用此触发函数,触发老师饿了信号
// emit t->hungry(); // 不带参
emit t->hungry("烧鸡");
}
4. 信号连接信号
不管是信号还是槽函数,connect的都是函数指针,teacherSignal,studentSlot,和QPushButton::clicked都是函数指针,可以相互关联。
按钮信号 -》 老师饿了信号 -》学生请客槽函数
基于上面的修改内容如下。
// 信号连接信号: 按钮信号连接老师饿了信号(信号就是函数指针)
void (Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
void (Student:: *studentSlot2)(void) = &Student::treat;
connect(t, teacherSignal2, s, studentSlot2); // 不带参
// disconnect(this->t, teacherSignal2, this->s, studentSlot2); // 断开老师信号和学生槽函数关联方式
// signal and slot arguments are not compatible.因为下面的btn绑定的slot是teacherSignal是带参的。
// connect(btn, &QPushButton::clicked, this->t, teacherSignal);
connect(btn, &QPushButton::clicked, this->t, teacherSignal2); // 信号关联不带参的信号(函数指针)
上面是信号关联不带参的信号,如何将信号关联带参信号呢?Lambda函数可以实现。
5. 拓展
- 信号可以连接信号;
- 一个信号可以连接多个槽函数;
- 多个信号可以连接同一个槽函数;
- 信号和槽函数的参数类型相同位置必须一一对应;
- 信号和槽函数的参数个数可以不一致:信号参数可以多余槽函数参数,即信号可以给,槽函数可以不用,但是槽函数如果要,信号就必须给,剩下位置的参数类型必须一一对应。
6. Lambda函数
lambda函数可以作为槽函数。
6.1 概要
完整表达式:[函数对象参数] (操作符重载函数参数) mutable ->函数返回类型 {函数体}
常用表达式:[=](){}
(1)函数对象参数:
- 空。没有使用任何函数对象参数;
- =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动按值传递了所有的局部变量);
- &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动按引用传递了所有的局部变量);
- this。函数体内可以使用Lambda所以类中的所有成员变量;
- a。将a变量按照值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const. 要修改a的拷贝(不是a本身),则可以添加mutable修饰符;
- &a。引用传递;
- a, &b。将a按值传递,b按引用传递;
- =, &a, &b。除了a和b按引用传递外,其他都按照值传递;
- &, a, b。除了a和b按照值传递外,其他都按照引用传递;
(2)操作符重载函数参数
标志重载的()操作符的参数,没有参数时可以省略。
(3)mutable
可以省略。
(4)函数返回类型
->返回类型,当返回值为void或者只有一处return地方(编译器可自动推断出返回类型)时,可以省略。
(5)函数体
可以为空{},但是不能省略。
6.2 简单示例
// Lambda函数声明
[=](){
btn->setText("按钮Lambda");
};
// 调用
[=](){
btn->setText("按钮Lambda");
}();
int m = 10;
connect(btn1, &QPushButton::clicked, this, [m]()mutable{m = 100+10; qDebug() << m;}); // 修改拷贝
connect(btn2, &QPushButton::clicked, this, [=](){qDebug() << m;}); // 上面没有修改m本身
// 有返回值的Lambda函数
int ret = []()->int{return 100;}(); // 返回int类型
qDebug() << "ret = " << ret;
//关闭窗口
// connect(btn1, &QPushButton::clicked, this, &Widget::close);
connect(btn1, &QPushButton::clicked, this, [&](){this->close();});
为啥接收者是this,因为槽函数是在当前类中定义的,属于当前类方法。
// 信号连接信号: 按钮信号连接老师饿了信号(信号就是函数指针)
// 不带参
void (Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
void (Student:: *studentSlot2)(void) = &Student::treat;
connect(t, teacherSignal2, s, studentSlot2);
connect(btn1, &QPushButton::clicked, this->t, teacherSignal2); // 信号关联不带参的信号(函数指针)
// 带参
connect(btn2, &QPushButton::clicked, this, [=](){ // 使用&程序奔溃了
emit this->t->hungry("fish"); // 有参槽函数
btn2->setText("bbbbb");
// this->close(); // 同时连接关闭窗口的槽函数
});