QT - 信号和槽

信号与槽概述:

  1. 在 Qt 中,用户和控件的每次交互过程称为一个事件

    • 比如 “用户点击按钮” 是一个事件, “用户关闭窗口” 也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出 “按钮被点击” 的信号,用户关闭窗口会发出 “窗口被关闭” 的信号。
  2. Qt 中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作

    • 例如,按钮所在的窗口接收到 “按钮被点击” 的信号后,会做出 “关闭自己” 的响应动作;再比如输入框自己接收到 “输入框被点击” 的信号后,会做出 “显示闪烁的光标,等待用户输入数据” 的响应动作。在 Qt 中,对信号做出的响应动作就称之为
  3. 信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来。比如, “按钮”和 “窗口” 本身是两个独立的控件,点击 “按钮” 并不会对 “窗口” 造成任何影响。通过信号和槽机制,可以将 “按钮” 和 “窗口” 关联起来,实现 “点击按钮会使窗口关闭” 的效果。

    Qt3

  4. 信号和槽:

    1. 系统提供的信号和槽–直接使用的信号和槽
    2. 自定义信号和槽–提供了方式可以自己 DIY 信号、槽

系统自带的信号与槽与connect:

  1. 使用 Qt 助手查找当前控件支持的系统信号和槽。

  2. 在 Qt 中,QObject 类提供了一个静态成员函数 connect(),该函数专门用来关联指定的信号函数和槽函数。

    1
    2
    3
    4
    5
    6
    7
    QMetaObject::Connection QObject::connect(
    const QObject *sender, // 发送信号的对象
    const char *signal, // 信号的签名
    const QObject *receiver, // 接收信号的对象
    const char *method, // 槽函数的签名
    Qt::ConnectionType type = Qt::AutoConnection // 连接类型
    );
    • sender 发送者 信号的发送者
    • signal 信号 具体发送的信号
    • receiver 接收者 事情发生之后需要做的事情的执行者
    • method 槽函数 接收者得到信号之后做的事情(信号、槽)
    • type 类型(可选) 信号的触发类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include "widget.h"
    Widget::Widget(QWidget *parent) : QWidget(parent)
    {
    setWindowTitle("Qt Demo");//设置窗口名称
    resize(QSize(800, 500)); //设置窗口尺寸
    MyButton1 = new QPushButton(this); //在堆区实例化一个按键,并将地址给类的一个属性
    MyButton1MyButton1->setText("点击关闭窗口"); //修改按键名称
    MyButton1->resize(QSize(500, 300)); //修改按键尺寸
    MyButton1->move(QPoint(150, 100));//把按钮放在父窗口的 (150,100) 位置
    //Qt 4 写法的 connect
    connect(MyButton1,SIGNAL(clicked()),this,SLOT(close()));
    //Qt 5 写法的 connect
    connect(MyButton1,&QPushButton::clicked,this,&Widget::close);
    }

自定义信号和槽:

  1. 自定义信号:
    1. 在头文件中需要使用 signals 关键字声明
    2. 返回值类型必须是 void
    3. 可以有参数,但参数不能有默认值
    4. 不需要实现,只需声明即可
    5. 使用关键字 emit 触发信号
    6. 可以重载
  2. 自定义槽:
    1. 在头文件中需要使用 slots 关键字声明
    2. 需要声明和实现
    3. 可以有参数,可以有默认值
    4. 返回值类型可以是任意类型
    5. 可以重载
  3. 注意:信号和槽没有特定配对关系:可以一个信号连接多个槽,也可以一个槽函数被多个信号连接

信号链接信号:

  1. 信号也可以直接连接信号。

  2. 语法:使用connect函数

    1. Qt4 语法:

      1
      2
      3
      4
      5
      6
      connect(
      senderObject, // 发送信号的对象指针
      SIGNAL(senderSignal()), // 发送的信号(必须用 SIGNAL() 宏包裹)
      receiverObject, // 接收信号的对象指针
      SIGNAL(receiverSignal()) // 触发的目标信号(必须用 SIGNAL() 宏包裹)
      );
    2. Qt5 语法:不支持,想实现信号连接信号,可以通过中间槽函数转发信号

  3. 例子:

    Qt4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include "widget.h"
    Widget::Widget(QWidget *parent) : QWidget(parent)
    {
    setWindowTitle("Qt Demo"); //设置窗口名称
    resize(QSize(800, 500)); //设置窗口尺寸
    pButtonLog = new QPushButton(this);
    pButtonLog->setText("输出 log"); //修改按键名称
    pButtonLog->resize(QSize(100, 60)); //修改按键尺寸
    pButtonLog->move(QPoint(150, 100));
    pButtonThree = new QPushButton(this);
    pButtonThree->setText("Three"); //修改按键名称
    pButtonThree->resize(QSize(100, 60)); //修改按键尺寸
    pButtonThree->move(QPoint(550, 100));
    //信号连接信号
    connect(pButtonLog,SIGNAL(clicked()),this,SIGNAL(sgSignal1()));
    connect(pButtonThree,SIGNAL(clicked()),this,SIGNAL(sgSignal2()));
    //信号连接槽函数
    connect(this,SIGNAL(sgSignal1()),this,SLOT(vWidgetSlotSignal1()));
    connect(this,SIGNAL(sgSignal2()),this,SLOT(vWidgetSlotSignal2()));
    }
    Widget::~Widget()
    {
    delete pButtonLog;
    }
    void Widget::vWidgetSlotSignal1(void)
    {
    qDebug() << "我是信号 1 触发的槽函数 1";
    }
    void Widget::vWidgetSlotSignal2(void)
    {
    qDebug() << "我是信号 2 触发的槽函数 2";
    }

    Qt5

信号和槽的传参:

  1. 信号和槽连接时, 形参列表必须一一对应(信号发生时,会将参数给对应的槽函数)

  2. 信号和槽函数支持重载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #ifndef WIDGET_H 
    #define WIDGET_H
    #include <QWidget>
    #include <QPushButton>
    #include <QDebug>
    #include <QLineEdit>
    class Widget : public QWidget
    {
    Q_OBJECT
    public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    QPushButton *pButtonLog;
    QLineEdit *Input;
    signals:
    void sgSignal_GetEditLineText(QString &Text);
    public slots:
    void vWidgetSlot_Button(void);
    void vWidgetSlot_Log(QString &Text);
    };
    #endif // WIDGET_H
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #include "widget.h"
    Widget::Widget(QWidget *parent) : QWidget(parent)
    {
    setWindowTitle("Qt Demo"); //设置窗口名称
    resize(QSize(800, 500)); //设置窗口尺寸
    pButtonLog = new QPushButton(this);
    pButtonLog->setText("clicked"); //修改按键名称
    pButtonLog->resize(QSize(100, 60)); //修改按键尺寸
    pButtonLog->move(QPoint(150, 100));
    Input = new QLineEdit(this);
    Input->setText("Input");
    Input->resize(QSize(500, 30));
    Input->move(QPoint(150, 35));
    //信号和槽函数的形参必须一致
    connect(pButtonLog,SIGNAL(clicked()),this,SLOT(vWidgetSlot_Button()));
    connect(this,SIGNAL(sgSignal_GetEditLineText(QString &)),this,SLOT(vWidgetSlot_Log(QString &)));
    }
    Widget::~Widget()
    {
    delete pButtonLog;
    }
    void Widget::vWidgetSlot_Button(void)
    {
    QString temp;temp = Input->text();
    emit sgSignal_GetEditLineText(temp);
    }
    void Widget::vWidgetSlot_Log(QString &Text)
    {
    qDebug() << "你输入的内容为: " << Text;
    }

Lambda表达式:

  1. Lambda 表达式是 C++11 引入的新特性,允许在代码中直接定义匿名函数。

  2. Lambda 的组成部分包括捕获列表、参数列表、可变规格、返回类型和函数体。

    1
    2
    3
    4
    5
    6
    7
    8
    只支持 Qt5 写法 
    [capture](parameters) mutable->return-type{
    statement
    }

    [捕获列表](参数列表) mutable(可选) 异常属性(可选) -> 返回类型(可选) {
    // 函数体
    }
    • 捕获列表:

      • []标识一个 Lambda 的开始,这部分必须存在,不能省

        Qt6

        Qt7

      • [=]:以值的方式捕获所有外部变量

      • [&]:以引用的方式捕获所有外部变量

      • [this]:捕获当前类的 this 指针

      • [a, &b]:混合捕获方式

    • 参数列表:

      • ()操作符:重载函数参数
      • 当()操作符,没有参数时,这部分可以省略。
      • 有参数时,可以通过按值(如: (a,b))和按引用(如: (&a,&b))两种方式进行传递。
    • 可变规格:(mutable)

      • mutable 声明,这部分可以省略。
      • 按值传递函数对象参数时,加上 mutable 修饰符后,可以修改按值传递进来的拷贝。
    • 返回类型:

      • 表示的是返回值类型,标识函数返回值的类型,当返回值是void,或者函数体中只有一处 return的地方(此时编译器可以自动推断出返回值类型)时, 这部分可以省略。
    • 函数体:

      • {},标识函数的实现,这部分不能省略,但是函数体可以为空。

参考链接: