2016-11-22 69 views
3

我有一個類ConnectionManager與方法get_wifi_ssids()必須返回一個SSID列表。問題是,要獲得這些SSID信號和插槽需要使用,但我不能找出一種方法來檢索該信息,而不必先退出該方法。如何使方法在返回之前發送信號並等待插槽?

這是從最低級到最高級使用的類的層次結構。

/** Controls wireless network card by commanding a software component "connman" via DBus. */ 
class WifiController : QObject { 
Q_OBJECT 

public: 

    void scan(); 
} 

/** Low level interface to network interfaces. */ 
class NetworkController : QObject { 
    Q_OBJECT 

public: 

    void scan_for_wifi() { 
     wifi_controller.scan(); 
     // When scan is finished it sends the 
     // NetworkTechnology::scanFinished signal. 
    } 

    // Gets info from cache. This cache is updated when a `scan()` happens. 
    QList<AccessPointInfo> get_available_access_points; 

private: 
    WifiController wifi_controller; 
} 

/** High level interface to network interfaces. */ 
class ConnectionManager { 
public: 
    QList<QString> get_wifi_ssids() { 
     netCtrlr.scan(); 
     // PROBLEM HERE: How do I wait for the `scanFinished` signal here, then 
     // continue execution and return the SSIDs from the recently-updated 
     // cache? 

     QList<AccessPointInfo> APs { netCtrlr.get_available_access_points() }; 
     QList<QSitrng> ssids { parseAPInfo(APs) }; 
     return ssids; 
    } 

private: 
    NetworkController netCtrlr; 
} 

我的應用程序的整體是在一個單一的線程。 「connman」由WifiConroller通過DBus命令,它是一個獨立的進程,很明顯在一個單獨的線程中。 GUI運行在一個單獨的進程中,我的應用程序通過DBus與它進行通信。

A QEventLoop是一個不好的解決方案,因爲根據this answer中的意見,它並不意味着在生產中使用,更多的是黑客攻擊。

+1

你真的必須**從'get_wifi_ssids()'返回列表嗎?爲什麼不在信號中發出這個列表呢?如果你想返回它們,你必須以某種方式在函數內停止,直到列表可用,或者通過啓動一個嵌套的事件循環(並且不鼓勵),或者通過阻塞整個線程(這更不鼓勵)。 – Mike

+1

我會建議發射一個信號與清單,當它是可用的。或者在你的類中存儲這個列表,然後發出一個信號讓你的觀察者從你的類中獲得最後一個可用的結果(比如''QIODevice :: readyRead()'](https://doc.qt.io/qt -5/qiodevice.html#readyRead)信號工作)。 – Mike

+0

@Mike嗯,這是一個很好的建議。但是,爲什麼說阻塞線程是一個壞主意(即使用自旋鎖等待掃描完成)呢?我沒有一個GUI線程(它在單獨的進程中運行,我的應用程序通過DBus與它通信)。 – DBedrenko

回答

2

您可以使用本地QEventLoop

QList<QString> get_wifi_ssids() { 
    QEvenLoop event; 
    // Stop event loop on signal 
    connect(&netCtrlr, SIGNAL(scanFinished()), &event, SLOT(quit())); 
    netCtrlr.scan(); 

    // run event loop 
    event.exec(); 

    QList<AccessPointInfo> APs { netCtrlr.get_available_access_points() }; 
    QList<QSitrng> ssids { parseAPInfo(APs) }; 
    return ssids; 
} 
+0

感謝您的幫助,但是'QEventLoop'並不意味着在生產中使用,並且更多是一種黑客(請參閱本文的評論([answer](http:// stackoverflow。com/a/3556525/797744)以獲取更多信息)。 – DBedrenko

+0

鏈接的答案中的鏈接不再工作,但我認爲他們指出了一些像這樣的文章:http://delta.affinix.com/2006/10/23/nested-eventloops/有安全的情況下使用一個事件循環。所以問題是,你的應用程序是如何設計的?或者,您也可以將進程標誌傳遞給'event.exec()'調用 –

+0

但是我同意Teemu Piippo在這種情況下GUI應用程序不應該阻塞。重新考慮製作無障礙設計。 –

5

由於掃描操作是異步的,你不能真正有掃描的SSID,並返回他們的方法,因爲等待掃描完成是阻塞操作。阻止操作阻止事件循環運行,並且信號信息得到處理。

您可以在get_wifi_ssids方法中有一個本地事件循環,但這會阻止應用程序的其餘部分無法工作。如果WiFi掃描有任何掛斷,程序將在其中凍結。

取而代之,重新設計該類,以便在需要時開始掃描,並且get_wifi_ssids返回有關接入點的最新信息。

+1

感謝您的建議。但是這個設計會是什麼樣子?在我的問題中的用例是當用戶請求我的應用程序連接到具有SSID和密碼的特定AP時。在嘗試連接之前,我必須掃描AP並檢查該SSID是否存在,否則請求中止。所以檢查取決於正在完成的掃描。 – DBedrenko

+0

如果您在用戶提供SSID和密碼之前尚未掃描接入點,則該程序需要進入掃描正在進行並且接口被鎖定的階段。例如,您可以告訴連接管理器緩存SSID和密碼,並在掃描完成時觸發檢查。 –

+0

該接口在另一個進程中運行,並且我的應用程序通過DBus與它進行通信,因此它不需要被鎖定(您的意思是阻止了嗎?)。即使此時用戶點擊「連接」觸發「掃描階段」,並檢查SSID是否存在仍然取決於正在完成的掃描(以獲取最新信息)。所以我不確定你的建議是什麼 – DBedrenko