tft每日頭條

 > 生活

 > 信号槽機制原理

信号槽機制原理

生活 更新时间:2024-12-21 19:02:18
前言

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_OBJECT public: 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"); }

代碼很簡單,運行結果如下:

信号槽機制原理(信号槽到底能不能有返回值)1

運行結果1

答案很明确:「信号槽可以有返回值。」 即槽的返回值會傳遞給信号,作為信号的返回值返回。 得出上述結論隻是第一步。

下面開始我們的第二問: 是否所有情況下,信号槽返回值都可以正常工作呢?

不同機制下的運行情況

在确保信号槽正常連接,并能夠成功發送接收的前提下,影響信号槽工作的因素就隻有在connect信号槽時指定的、信号槽的連接方式。 我們知道信号槽的連接方式有以下幾種:

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

嚴格來說,最後一個UniqueConnection是用來保證信号槽一對一連接的,從實現機制上并不算是一種連接方式。所以我們依次測試前四種連接方式,得到了不同的結果。

  • AutoConnection

默認連接方式,輸出和前面的輸出相同。

信号槽機制原理(信号槽到底能不能有返回值)2

運行結果-AutoConnection

  • DirectConnection

即代碼改成:

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

此種為直接連接方式,相當于函數調用,輸出結果為

信号槽機制原理(信号槽到底能不能有返回值)3

運行結果-DirectConnection

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

  • QueuedConnection

即代碼改成:

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

此種為異步消息方式連接,用于将消息發向具有消息循環的線程,以消息的方式傳遞信号,信号槽既可以在同一個線程,也可以在不同的線程。這裡在主線程進行了測試,輸出結果為

信号槽機制原理(信号槽到底能不能有返回值)4

運行結果-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_OBJECT public: 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; }

輸出結果:

信号槽機制原理(信号槽到底能不能有返回值)5

運行結果-BlockingQueuedConnection

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

「綜上所述:」

從不同的信号槽連接機制的角度看,不管是在多線程還是單線程,隻要是異步非阻塞方式連接的信号槽,都無法獲取返回值;隻要是同步阻塞方式連接的信号槽,都可以獲取返回值。 原理上也比較容易理解。非阻塞方式連接的信号槽,信号返回時,槽還沒有返回,如何将返回值傳遞回去呢,邏輯上是行不通的。隻有在同步阻塞的情況下,由響應槽先返回,将返回值傳遞給信号以後,信号再返回,這樣便實現了信号槽的返回值功能。

不同使用方式下的運行情況

上面講解了不同連接機制下的運行情況。在實際使用時,也有一些情況需要考慮。 比如,一個帶返回值的信号,連接到多個帶相同類型返回值的槽,最後的返回值是什麼? 新建Qt Widgets Application測試,下面是測試代碼:

mainwindow.h文件

#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: 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()。 輸出結果為:

信号槽機制原理(信号槽到底能不能有返回值)6

運行結果-一信号對多槽連接

可見,最後輸出結果隻有一個,另外一個結果丢失或被覆蓋,這是符合邏輯的,因為返回值隻能有一個,最後的結果會覆蓋前面的結果。

結語

本文從實現機制角度、應用場景角度對信号槽返回值功能進行了實驗和分析,基本涵蓋了工作中的各種使用情形,弄清楚了在不同的情況下,信号槽返回值功能的表現情況。一句話總結就是:

隻有同步阻塞連接時才能使用信号槽返回值,且不管有幾個槽隻能有一個返回值。

正是因為信号槽返回值限制太多,所以才幾乎沒有人使用。但是我們的研究沒有白費,通過研究的過程也加深了我們對信号槽機制的理解。

關于信号槽,有太多話題可以聊,後面會推出更多相關文章,敬請關注!

本文首發于微信公衆号“Qt未來工程師”。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved