【探究】信号槽到底能不能有返回值?

前言

Qt信号槽到底可不可以有返回值呢?问了下身边的同事,有的人说可以,有的人说不可以。在实际项目中,确实没看到过有人使用带返回值的信号槽,可以说存在感很低。平时大家工作也比较忙,所以也没有时间去较真信号槽到底能不能有返回值。今天就一起带大家较真一下,看一看信号槽能否有返回值。如果可以有返回值,那么又有哪些限制导致大家都不用它呢?

提示:本文代码略多,但是都很简单,请耐心阅读。

上代码

新建一个Qt Widgets Application,在Qt为我们生成的主窗口.h文件中,添加信号槽的声明,分别是

  • 信号QString sigCurrentTime(),用于获取当前时间的信号
  • 槽QString slotCurrentTime(),用于获取当前时间的槽

完整代码如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
namespace Ui { class MainWindow; }class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:// 用于获取当前时间的信号QString sigCurrentTime();private slots:// 用于获取当前时间的槽QString slotCurrentTime();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

在.cpp文件中,建立信号槽连接,实现槽函数,发送信号并打印返回值。完整代码如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(this, SIGNAL(sigCurrentTime()), this, SLOT(slotCurrentTime()));qDebug() << "Current Time:" << sigCurrentTime();
}MainWindow::~MainWindow()
{delete ui;
}QString MainWindow::slotCurrentTime()
{return QDateTime::currentDateTime().toString("yyyyMMdd hh:mm:ss");
}

代码很简单,运行结果如下:

答案很明确:信号槽可以有返回值。 即槽的返回值会传递给信号,作为信号的返回值返回。
得出上述结论只是第一步。

下面开始我们的第二问:
是否所有情况下,信号槽返回值都可以正常工作呢?

不同机制下的运行情况

在确保信号槽正常连接,并能够成功发送接收的前提下,影响信号槽工作的因素就只有在connect信号槽时指定的、信号槽的连接方式。
我们知道信号槽的连接方式有以下几种:

  • AutoConnection,
  • DirectConnection,
  • QueuedConnection,
  • BlockingQueuedConnection,
  • UniqueConnection

严格来说,最后一个UniqueConnection是用来保证信号槽一对一连接的,从实现机制上并不算是一种连接方式。所以我们依次测试前四种连接方式,得到了不同的结果。

  • AutoConnection

默认连接方式,输出和前面的输出相同。

  • DirectConnection

即代码改成:

connect(this, SIGNAL(sigCurrentTime()), this, SLOT(slotCurrentTime()), Qt::DirectConnection);

此种为直接连接方式,相当于函数调用,输出结果为

可以看到,返回值可以正常返回。

  • QueuedConnection

即代码改成:

connect(this, SIGNAL(sigCurrentTime()), this, SLOT(slotCurrentTime()), Qt::QueuedConnection);

此种为异步消息方式连接,用于将消息发向具有消息循环的线程,以消息的方式传递信号,信号槽既可以在同一个线程,也可以在不同的线程。这里在主线程进行了测试,输出结果为

线程间信号槽返回值测试结果相同。可以看到异步消息方式,信号槽返回值无法工作。

  • BlockingQueuedConnection

此种方式用于多线程环境下,一个线程向另外一个线程发送信号,并阻塞等待槽执行完成。
下面是多线程的测试代码,添加了一个新的QObject子类:ObjectInSubThread,用于在子线程中运行。代码如下(想要直接看结果的同学可直接跳过):

ObjectInSubThread.h头文件

#ifndef OBJECTINSUBTHREAD_H
#define OBJECTINSUBTHREAD_H#include <QObject>class ObjectInSubThread : public QObject
{Q_OBJECT
public:explicit ObjectInSubThread(QObject *parent = nullptr);private slots:// 用于获取当前时间的槽QString slotCurrentTime();
};#endif // OBJECTINSUBTHREAD_H

ObjectInSubThread.cpp实现文件

#include "ObjectInSubThread.h"
#include <QDateTime>ObjectInSubThread::ObjectInSubThread(QObject *parent) : QObject(parent)
{}QString ObjectInSubThread::slotCurrentTime()
{return QDateTime::currentDateTime().toString("yyyyMMdd hh:mm:ss");
}

同时修改mainwindow类的代码:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include "ObjectInSubThread.h"
#include <QThread>namespace Ui { class MainWindow; }class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:// 用于获取当前时间的信号QString sigCurrentTime();
private:Ui::MainWindow *ui;ObjectInSubThread *m_obj_in_subthread; /* 子线程中的对象 */QThread m_sub_thread; /* 子线程 */
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), m_obj_in_subthread(0)
{ui->setupUi(this);// 创建子线程对象m_obj_in_subthread = new ObjectInSubThread;// 移动到子线程中m_obj_in_subthread->moveToThread(&m_sub_thread);// 启动子线程消息循环m_sub_thread.start();// 建立信号槽连接connect(this, SIGNAL(sigCurrentTime()), m_obj_in_subthread, SLOT(slotCurrentTime()), Qt::BlockingQueuedConnection);qDebug() << "Current Time:" << sigCurrentTime();
}MainWindow::~MainWindow()
{delete ui;m_sub_thread.quit();m_sub_thread.wait();if (m_obj_in_subthread) delete m_obj_in_subthread;
}

输出结果:

表明信号槽返回值功能可以正常工作。

综上所述:

从不同的信号槽连接机制的角度看,不管是在多线程还是单线程,只要是异步非阻塞方式连接的信号槽,都无法获取返回值;只要是同步阻塞方式连接的信号槽,都可以获取返回值。
原理上也比较容易理解。非阻塞方式连接的信号槽,信号返回时,槽还没有返回,如何将返回值传递回去呢,逻辑上是行不通的。只有在同步阻塞的情况下,由响应槽先返回,将返回值传递给信号以后,信号再返回,这样便实现了信号槽的返回值功能。

不同使用方式下的运行情况

上面讲解了不同连接机制下的运行情况。在实际使用时,也有一些情况需要考虑。
比如,一个带返回值的信号,连接到多个带相同类型返回值的槽,最后的返回值是什么?
新建Qt Widgets Application测试,下面是测试代码:

mainwindow.h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>namespace Ui { class MainWindow; }class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:// 用于获取当前时间的信号QString sigCurrentTime();private slots:// 用于获取当前时间的槽QString slotCurrentTime1();// 用于获取当前时间的槽QString slotCurrentTime2();private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// sigCurrentTime分别连接到槽slotCurrentTime1()和槽slotCurrentTime2()connect(this, SIGNAL(sigCurrentTime()), this, SLOT(slotCurrentTime1()));connect(this, SIGNAL(sigCurrentTime()), this, SLOT(slotCurrentTime2()));qDebug() << "Current Time:" << sigCurrentTime();
}MainWindow::~MainWindow()
{delete ui;
}QString MainWindow::slotCurrentTime1()
{return QDateTime::currentDateTime().toString("yyyyMMdd hh:mm:ss") + " slotCurrentTime1";
}QString MainWindow::slotCurrentTime2()
{return QDateTime::currentDateTime().toString("yyyyMMdd hh:mm:ss") + " slotCurrentTime2";
}

如上所示,信号sigCurrentTime分别连接到了槽slotCurrentTime1()和槽slotCurrentTime2()。
输出结果为:

可见,最后输出结果只有一个,另外一个结果丢失或被覆盖,这是符合逻辑的,因为返回值只能有一个,最后的结果会覆盖前面的结果。

结语

本文从实现机制角度、应用场景角度对信号槽返回值功能进行了实验和分析,基本涵盖了工作中的各种使用情形,弄清楚了在不同的情况下,信号槽返回值功能的表现情况。一句话总结就是:

只有同步阻塞连接时才能使用信号槽返回值,且不管有几个槽只能有一个返回值。

正是因为信号槽返回值限制太多,所以才几乎没有人使用。但是我们的研究没有白费,通过研究的过程也加深了我们对信号槽机制的理解。

关于信号槽,有太多话题可以聊,后面会推出更多相关文章,敬请关注!


本文由公众号“Qt未来工程师”原创首发。

【探究】信号槽到底能不能有返回值?相关推荐

  1. Qt 使用C++特性“引用” - 获得槽函数的返回值

    QT信号与槽中,槽函数是可以定义有返回值的,但是我们都是在connect函数中进行调用,那么该如何获得调用后的槽函数的返回值呢? 答案是不可能获得了的! 槽函数虽然可以定义返回值类型,但其实他和信号一 ...

  2. qdialog 返回值_QDialog 窗口级别模态(续)

    QDialog::open() 考虑到前文所述的问题,我们对比一下应用程序级别模态的实现.当windowModality是Qt::NonModal的时候,只需调用QDialog::exec()就可以很 ...

  3. 探讨read的返回值的三种情况

    今天探讨一个很看似简单的API "read"的返回值问题.read的返回值有哪几个值?每个值又是在什么情况下发生的? 先问一下男人吧:man 2 read RETURN VALUE ...

  4. 11. 函数进阶-函数名,返回值,作用域

    11. 函数进阶 目标:掌握函数相关易错点 & 项目开发必备技能. 概要: 参数的补充 函数名,函数名到底是什么? 返回值和print,傻傻分不清楚. 函数的作用域 1.参数的补充 在函数基础 ...

  5. system函数返回值,Linux

    理论 我们先看下man手册是怎么说的man system RETURN VALUEThe value returned is -1 on error (e.g., fork(2) failed), a ...

  6. boost::signals2模块实现用于从槽返回值到信号调用的示例程序

    boost::signals2模块实现用于从槽返回值到信号调用的示例程序 实现功能 C++实现代码 实现功能 boost::signals2模块实现用于从槽返回值到信号调用的示例程序 C++实现代码 ...

  7. QT源码剖析-QT对象通信机制信号槽的绑定具体实现

    本文详细介绍QT核心机制之一 信号和槽. 我们在此根据Qt源代码一步一步探究其信号槽的实现过程. 核心知识点: 模板元编程技术. Qt moc预编译机制. QObject类. 目录 1. QObjec ...

  8. Linux+Qt 下同一数据空间vfork多进程间通信的一种高效便捷方式(信号槽直接调用)

    Linux+Qt 下同一数据空间vfork多进程间通信的一种高效便捷方式(信号槽直接调用) 概述 传统的多进程间通信往往非常麻烦,采用的方法比如管道,共享内存,socket,文件等,大都非常繁琐, 1 ...

  9. 6.QT信号槽的时序分析

    前面已经分析了元对象系统.MOC文件和信号槽的连接,本文分析下信号槽的时序 信号的触发通过emit关键字触发,以sigf1为例,通常是这样的 emit sigf1(t1) emit就是个空宏,在qob ...

最新文章

  1. matlab 读取WAV文件
  2. 【渝粤教育】国家开放大学2018年秋季 0350-21T幼儿园课程论 参考试题
  3. 【简记】HTML CSS 的一些要点(不定时更新)
  4. 从包中构建瓦片服务器
  5. 软件验收标准和验收方法_卫生间防水验收标准
  6. 文件隐藏服务器版本信息吗,隐藏/屏蔽服务器信息与web软件版本信息
  7. ftp服务器端的编写 c语言6,linux下c语言编写的ftp服务器
  8. Promise面试题
  9. NB: JAVA_HOME should point to a JDK not a JRE
  10. 用python实现黑客帝国数字雨效果
  11. 成为软件架构师需要什么?
  12. 敏捷史话(十三):我被 Facebook 解雇了——Kent Beck
  13. Windows 下rsync同步数据报错7456
  14. ios linux远程控制软件,ios手机远程控制 ToDesk 1.0.9 iOS/iPad 版下载(远程控制软件)-520下载...
  15. 大数据最佳实践-hbase
  16. 几个命令查看ELF文件的“秘密”
  17. 人脸识别被滥用,三人成虎不足挂齿?
  18. 循环结构中“当型”与“直到型”判断的理解
  19. 数据库系统设计与原理
  20. 用地图说话 在商业分析与演示中运用Excel数据地图 全彩

热门文章

  1. ???--???二进制变换
  2. css 水平垂直居中那些事
  3. Ubuntu 12.04(32位)安装Oracle 11g(32位)全过程以及几乎所有问题的解决办法
  4. Thread类和Runnable接口
  5. 【数据结构与算法】【算法思想】【联系与区别】回溯 贪心 动态规划 分治
  6. [Leetcode][第679题][JAVA][24点游戏][回溯][暴力]
  7. python工厂模式 理解_浅谈Python设计模式 - 抽象工厂模式
  8. dalvik虚拟机与Java区别_05 Android---java虚拟机跟dalvik虚拟机的区别(从01开始点点入门,视频+笔记)...
  9. 3493. 最大的和
  10. pythonwhile输出每一个余数_Python 基础 - day02-3