Qt中的信号和槽详解

一、背景介绍

信号和槽用于两个对象之间的通信。信号和槽机制是Qt的核心特征,也是Qt不同于其他开发框架的最突出特征。在GUI编程中,当改变了一个部件时,总希望其他部件也能了解到该变化。更一般来说,我们希望任何对象都可以和其他对象进行通信。例如,用户单击了关闭按钮,则希望可以执行窗口的close()函数来关闭窗口。为了实现对象间的通信,一些工具包中使用了回调(callback)机制,而在Qt中使用了信号和槽来进行对象间的通信。当一个特殊的事情发生时便可以发射一个信号,比如按钮被单击就发射clicked()信号;而槽就是一个函数,它在信号发射后被调用来响应这个信号。Qt的部件类中已经定义了一些信号和槽,但是更常用的做法是子类化部件,然后添加自定义的信号和槽来实现想要的功能。

二、Signals and Slots(信号和槽)

信号槽类似于软件设计模式中的观察者模式,(观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。)被观察者发出的信号(signal),观察者收到自己注册监听signal,就通过槽(slot)关联的槽函数function实现动作操作。(这里提一句,Qt 的信号槽使用了额外的处理来实现,并不是 GoF 经典的观察者模式的实现方式)

Qt中的信号和槽详解_第1张图片

信号槽优点:

(1)类型安全

信号的参数类型、参数个数需要和槽函数的参数类型和参数个数一致。槽函数的个数也可以少于信号的参数个数,但缺少的参数必须是信号参数的最后一个或最后几个。

(2)松散耦合

信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调用。支持信号槽机制的类或者父类必须继承于QObject。

(3)效率

与回调函数相比,信号和槽稍微慢一些,因为它们提供了更高的灵活性,尽管在实际应用程序中差别不大。通过信号调用的槽函数比直接调用的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调用速度对性能要求不是非常高的场景是可以忽略的,是可以满足绝大部分场景。

三、实例

下面通过一个简单的例子来进一步讲解信号和槽的相关知识。这个例子实现的效
果是:在主界面中创建一个对话框,在这个对话框中可以输入数值,当单击“OK”按钮
时关闭对话框并且将输入的数值通过信号发射出去,最后在主界面中接收该信号并且
显示数值。程序的运行效果如图所示。

Qt中的信号和槽详解_第2张图片
新建Qt Widgets应用,
项目名称为mysignalslot,基类选择QWidget,类名保持Widget不变。项目建立完成
后,向项目中添加新文件,模板选择Qt分类中的“Qt设计师界面类”,界面模板选择Di-
alog without Buttons,类名设置为MyDialog。

Qt中的信号和槽详解_第3张图片

完成后,在ui文件中添加一个pushButton,一个spinBox

在mydialog.h文件中添加代码 

#ifndef MYDIALOG_H
#define MYDIALOG_H

#include 

namespace Ui {
class MyDialog;
}

class MyDialog : public QDialog
{
    Q_OBJECT // 重要

public:
    explicit MyDialog(QWidget *parent = nullptr);
    ~MyDialog();

private:
    Ui::MyDialog *ui;
signals:
    void DialogReturn(int); // 信号函数
private slots:
    void on_pushButton_clicked();
};

#endif // MYDIALOG_H

cpp文件

#include "mydialog.h"
#include "ui_mydialog.h"

MyDialog::MyDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::MyDialog)
{
    ui->setupUi(this);
}

MyDialog::~MyDialog()
{
    delete ui;
}

void MyDialog::on_pushButton_clicked()
{
    int value = ui->spinBox->value();
    emit DialogReturn(value);
    close();
}

 声明一个信号要使用signals关键字,在signals前面不能用public、private和protected等限定符,因为信号默认是public函数,可以从任何地方进行发射,但是建议只在定义该信号的类及其子类中发射该信号。信号只用声明,不需要也不能对它进行定义实现。

还要注意,信号没有返回值,只能是void类型的。因为只有QObject类及其子类派生的类才能使用信号和槽机制,这里的MyDialog类继承自QDialog类,QDialog类又继承自QWidget类,QWidget类是QObject类的子类,所以这里可以使用信号和槽。不过,使用信号和槽还必须在类声明的最开始处添加Q_OBJECT宏,在这个程序中,类的声明是自动生成的,已经添加了这个宏。

槽就是普通的C++函数,可以像一般的函数一样使用。声明槽要使用slots关键
字,一个槽可以是private、.public或者protected类型的,槽也可以被声明为虚函数,这
与普通的成员函数是一样的。槽的最大特点就是可以和信号关联。
下面打开widget..ui文件,向界面上拖入一个Label部件,更改其文本为“get value is:”。然后进入widget..cpp文件添加头文件#include“mydialog.h”, 

Widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include 

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;
private slots:
    void ShowValue(int value);
};
#endif // WIDGET_H

cpp文件

#include "widget.h"
#include "ui_widget.h"
#include "mydialog.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    MyDialog* myDialog = new MyDialog(this);
    connect(myDialog,SIGNAL(DialogReturn(int)),this,SLOT(ShowValue(int)));
    myDialog->show();

};

Widget::~Widget()
{
    delete ui;
}

void Widget::ShowValue(int value)
{
    ui->label->setText(tr("get value is : %1").arg(value));
}

现在运行程序查看效果。

Qt中的信号和槽详解_第4张图片

这个程序自定义了信号和槽,可以看到它们使用起来很简单,只需要进行关联,然后在适当的时候发射信号即可。这里列举一下使用信号和槽应该注意的几点:

  1. 需要继承自QObject或其子类;
  2. 在类声明的最开始处添加Q OBJECT宏;
  3. 槽中参数的类型要和信号参数的类型相对应,且不能比信号的参数多;
  4. 信号只用声明,没有定义,且返回值为void类型。 

参考:

Qt中的信号和槽详解_徐kun按门铃的博客-CSDN博客_qt 信号槽

Qt的信号槽基本用法总结_夜雨听萧瑟的博客-CSDN博客_qt信号槽

你可能感兴趣的:(Qt,qt,开发语言)