qt 信号和槽的简明使用

目录

1. connect函数 

2. 自定义的信号和槽

3. 带参数的信号和槽

4. 信号连接信号

5. 拓展

6. Lambda函数

6.1 概要

6.2 简单示例


qt 信号和槽的简明使用_第1张图片

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 

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();
}

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 

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("烧鸡");
}

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();  // 同时连接关闭窗口的槽函数
    });

你可能感兴趣的:(qt,qt)