Qt之信号和槽的机制

前言       

        在 C++ 中,对象与对象之间产生联系要通过调用成员函数的方式。但是在 Qt中,Qt提供了一种新的对象间的通信方式,即信号和槽机制。在GUI编程中,通常希望一个窗口部件的一个状态的变化会被另一个窗口部件知道,为了实现这种效果且取代老式的回调函数,信号和槽机制应运而生,Qt通过 QObject 提供信号和槽的功能。
        信号和槽的核心原理很简单,当某个事件发生之后,如按钮检测到自己被单击了一下,它就会广播出一个信号。如果有对象对这个信号感兴趣,就使用连接函数,将想要处理的信号和自己的一个函数(称为槽函数)进行绑定并处理这个信号。也就是说,当信号被发出时,与之连接的槽即函数会自动被回调。


信号和槽的使用 

        槽的本质就是类的成员函数,其参数可以是任意类型,可以是虚函数,可以被重载。槽通常和信号连接在一起,当信号被发出时,与这个信号连接的槽函数就会被调用,其语法如下。

connect(sender, SIGNAL(signal), receiver, SLOT(slot));

具体参数如下:

  • sender:发出信号的对象。
  • signal:发送对象发出的信号。
  • receiver:接收信号的对象。
  • slot:接收对象在接收到信号之后所需要调用的函数。

        参数中的 sender 就是指向发送信号的对象的指针,receiver 是指向包含槽函数的对象的指针。signal是被发送的信号,slot是接收信号后调用的槽函数,均为不带参数的函数名。SIGNAL()和SLOT()会将其参数转换为字符串,即将函数名转换为字符串,将这个字符串传入 connect()中。

信号和槽的连接比较随意,一个信号可以连接多个槽。
connect(sender, SlGNAL(sianal), receiverA, SLOT(slotA));
connect(sender, SlGNAL(signal), receiverB, SLOT(slotB));

也可以多个信号连接同一个槽。
connect(senderA, SIGNAL(signalA), receiver, SLOT(slot));
connect(senderB, SlGNAL(signalB), receiver, SLOT(slot));

一个信号还可以连接另外一个信号。
connect(sender, SIGNAL(signalA), receiver, SlGNAL(signalB));

当 sender对象发送信号给signalA时,触发receiver对象发送信号给signalB。同时,信号和槽之间的连接可以被移除。
disconnect(sender, SlGNAL(signal),receiver, SLOT(slot));

 Qt 信号和槽机制的优缺点如下:

  • Ot的信号和槽机制的引用可减少程序员编写的代码量。
  • Qt的信号可以对应多个槽(它们的调用顺序随机),也可以多个槽映射一个信号
  • Qt的信号和槽的建立与解除绑定十分自由。
  • 信号和槽同真正的回调函数比起来,时间的耗损还是很大的,在嵌入式实时系统中应当慎用。
  • 信号和槽的参数限定很多,如不能携带模板类参数、不能出现宏定义等。

 用一个例子来解释一下信号和槽的机制:

 新建一个基类 QWidget的 Qt 图形应用项目。

 在widgt.h文件中:

#ifndef WIDGT_H
#define WIDGT_H
#include<QPushButton>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class widgt; }
QT_END_NAMESPACE
class widgt : public QWidget
{
    Q_OBJECT//只有继承了QObject类的类,才具有信号和槽;
            //宏QOBJECT是任何实现信号、槽或属性的强制性要求
public:
    widgt(QWidget *parent = nullptr);
    ~widgt();
private:
    Ui::widgt *ui;
    QPushButton button;
};
#endif // WIDGT_H

在widgt.cpp文件中:

#include "widgt.h"
#include "ui_widgt.h"
widgt::widgt(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::widgt)
{
    this->resize(200,200);//修改默认的窗口尺寸
    button.setParent(this);// 绑定窗口和按钮
    button.setText("关闭窗口");// 按钮框中文本
    button.move(48,64);// 定义按钮的位置,以左上角为原点,px 为单位
    connect(&button,&QPushButton::pressed,this,&widgt::close);
}
widgt::~widgt()
{
    delete ui;
}

在main.cpp文件中:

#include "widgt.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    widgt w;
    w.show();
    return a.exec();
}

        在这些代码中,button是信号的发送方;信号为QPushButton基类定义的pressed信号;信号接收方为 widgt w,即在构造函数中写为 this;槽函数为基类QWidget定义的 close()函数。这样,在单击“close”按钮时,该按钮 button会发送一个pressed信号,pressed信号会触发w中继承的 close()函数,实现结束程序的功能。 


自定义信号和槽函数

         connect()函数可用来连接系统提供的信号和槽。但是,Qt的信号和槽机制并不是仅能使用系统提供的那部分,程序员也可设计自己的信号和槽。

        首先,创建一个基于基类 QWidget的 Qt图形应用项目,添加一个新的类,类名为“Widget”继承于 Qt 的基类 QWidget。

 在widgt.h文件中:

#ifndef WIDGET_H
#define WIDGET_H
#include <QLabel>
#include <QPushButton>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    void MySlot();//定义
    ~Widget();
private:
    Ui::Widget *ui;
    QPushButton button;
    QLabel label;
};
#endif // WIDGET_H

 在widgt.cpp文件中:

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    this->resize(240,320);
    button.setParent(this);
    button.move(40,50);
    button.setText("更换内容");
    label.setParent(this);
    label.move(40,100);
    label.setText("更换前");
    connect(&button,&QPushButton::pressed,this,&Widget::MySlot);
}
void Widget::MySlot()
{
    label.setText("更换后");
}
Widget::~Widget()
{
    delete ui;
}

在main.cpp文件中

#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

结果:

 


自定义信号和槽函数的要点

自定义槽函数要点如下。

  • 槽函数可以传入参数,但没有返回值。
  • 在 Qt5 中,任何成员函数、静态成员函数、全局函数及 Lambda 表达式都可以作为槽函数。与信号函数不同,槽函数必须自己完成实现代码。槽函数就是普通的成员函数,因此也会受到public、private 等访问控制关键字的影响。( 如果信号是私有的,这个信号就不能在类的外面连接,而类中本来就可以直接传递,因此这种限制也就没有任何意义。)

 自定义信号和槽需要注意的事项有以下几个。

  • 发送者和接收者都需要 QObject的派生类(当然,槽函数是全局函数、Lambda 表达式等无须接收者的时候除外 )。
  • 使用 signals 标记信号,信号是一个函数声明,返回 void,不需要实现函数代码。
  • 使用 emit 在恰当的位置发送信号。
  • 可以在 main.cpp 中使用QObject::connect()函数连接信号和槽函数。
  • 任何成员函数、静态成员函数、全局函数及Lambda 表达式都可以作为槽函数。

 Q_OBJECT

        在Qt中,如果一个类要使用信号和槽的功能,就必须在其中声明Q_OBJECT,类的定义的第一行就写上了 Q_OBJECT。不管是不是使用信号和槽,都应该添加 O_OBJECT宏,这个宏为类提供了信号和槽机制、国际化机制以及Qt提供的不基于 C++ RTTI的反射能力。其他很多操作都会依赖于这个宏,即使类中不需要使用信号和槽,也需要添加这个宏,否则会出现编译错误。

相关推荐

  1. Qt信号机制

    2024-04-07 06:32:04       34 阅读
  2. QT 信号

    2024-04-07 06:32:04       59 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-07 06:32:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-07 06:32:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-07 06:32:04       82 阅读
  4. Python语言-面向对象

    2024-04-07 06:32:04       91 阅读

热门阅读

  1. WebKit结构简介

    2024-04-07 06:32:04       38 阅读
  2. 什么是MariaDB

    2024-04-07 06:32:04       38 阅读
  3. 人工智能时代呼唤科技与创新成为新榜样

    2024-04-07 06:32:04       36 阅读
  4. 工业物联网中的区块链技术应用

    2024-04-07 06:32:04       39 阅读
  5. 0基础如何成功步入IT行业

    2024-04-07 06:32:04       129 阅读
  6. C# 构建可定时关闭的异步提示弹窗

    2024-04-07 06:32:04       43 阅读