• Qt中的信号和槽详解


    一、背景介绍

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

    二、Signals and Slots(信号和槽)

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

    信号槽优点:

    (1)类型安全

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

    (2)松散耦合

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

    (3)效率

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

    三、实例

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


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

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

    在mydialog.h文件中添加代码 

    1. #ifndef MYDIALOG_H
    2. #define MYDIALOG_H
    3. #include
    4. namespace Ui {
    5. class MyDialog;
    6. }
    7. class MyDialog : public QDialog
    8. {
    9. Q_OBJECT // 重要
    10. public:
    11. explicit MyDialog(QWidget *parent = nullptr);
    12. ~MyDialog();
    13. private:
    14. Ui::MyDialog *ui;
    15. signals:
    16. void DialogReturn(int); // 信号函数
    17. private slots:
    18. void on_pushButton_clicked();
    19. };
    20. #endif // MYDIALOG_H

    cpp文件

    1. #include "mydialog.h"
    2. #include "ui_mydialog.h"
    3. MyDialog::MyDialog(QWidget *parent) :
    4. QDialog(parent),
    5. ui(new Ui::MyDialog)
    6. {
    7. ui->setupUi(this);
    8. }
    9. MyDialog::~MyDialog()
    10. {
    11. delete ui;
    12. }
    13. void MyDialog::on_pushButton_clicked()
    14. {
    15. int value = ui->spinBox->value();
    16. emit DialogReturn(value);
    17. close();
    18. }

     声明一个信号要使用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文件

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. QT_BEGIN_NAMESPACE
    5. namespace Ui { class Widget; }
    6. QT_END_NAMESPACE
    7. class Widget : public QWidget
    8. {
    9. Q_OBJECT
    10. public:
    11. Widget(QWidget *parent = nullptr);
    12. ~Widget();
    13. private:
    14. Ui::Widget *ui;
    15. private slots:
    16. void ShowValue(int value);
    17. };
    18. #endif // WIDGET_H

    cpp文件

    1. #include "widget.h"
    2. #include "ui_widget.h"
    3. #include "mydialog.h"
    4. Widget::Widget(QWidget *parent)
    5. : QWidget(parent)
    6. , ui(new Ui::Widget)
    7. {
    8. ui->setupUi(this);
    9. MyDialog* myDialog = new MyDialog(this);
    10. connect(myDialog,SIGNAL(DialogReturn(int)),this,SLOT(ShowValue(int)));
    11. myDialog->show();
    12. };
    13. Widget::~Widget()
    14. {
    15. delete ui;
    16. }
    17. void Widget::ShowValue(int value)
    18. {
    19. ui->label->setText(tr("get value is : %1").arg(value));
    20. }

    现在运行程序查看效果。

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

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

    参考:

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

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

  • 相关阅读:
    为什么用葫芦儿派盘取代U盘?
    CN_MAC介质访问控制子层@CSMA协议
    EasyCVR视频监控+AI智能分析网关如何助力木材厂安全生产?
    10.26ALP论文原代码请稿
    服务器密码以及用户名怎么修改
    408数据结构,怎么练习算法大题?
    寒假训练——第三周(递推公式)
    spring boot整合mybatis,mybatis generator ,自定义typhandler
    pod之间的通信
    编程高手必学的内存知识03:深入理解堆
  • 原文地址:https://blog.csdn.net/sinat_31608641/article/details/126694691