目录
1. connect函数
2. 自定义的信号和槽
3. 带参数的信号和槽
4. 信号连接信号
5. 拓展
6. Lambda函数
6.1 概要
6.2 简单示例
点击按钮,关闭Widget
connect(btn, &QPushButton::clicked, this, &Widget::close);
四个参数:
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);
}
信号:
槽:
举个例子:下课了,触发老师发送饿了的信号,学生接受信号并进行请客吃饭事件。
程序:调用classIsOver(),触发老师hungry(), 学生接受信号并treat();
Teacher类:
#ifndef TEACHER_H
#define TEACHER_H
#include
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
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
#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();
}
预备知识点:
(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
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
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
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
// 下课后,老师饿了,学生请客吃饭。
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("烧鸡");
}
不管是信号还是槽函数,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函数可以实现。
lambda函数可以作为槽函数。
完整表达式:[函数对象参数] (操作符重载函数参数) mutable ->函数返回类型 {函数体}
常用表达式:[=](){}
(1)函数对象参数:
(2)操作符重载函数参数
标志重载的()操作符的参数,没有参数时可以省略。
(3)mutable
可以省略。
(4)函数返回类型
->返回类型,当返回值为void或者只有一处return地方(编译器可自动推断出返回类型)时,可以省略。
(5)函数体
可以为空{},但是不能省略。
// 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(); // 同时连接关闭窗口的槽函数
});