2016-11-22 91 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