2016-11-22 7 views
3

私はget_wifi_ssids()のメソッドConnectionManagerを持っており、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; 
} 

私のアプリケーション全体が1つのスレッドに含まれています。 "connman"は、DBusを介してWifiConrollerによって命令されています。それは明らかに別のスレッドに分かれています。 GUIは別のプロセスで実行され、私のアプリケーションはDBus経由で通信します。

QEventLoopは、生産に使用されることを意図したものではなく、ハックの多くであるため、悪い解決策です。this answerのコメントによると、

+1

本当に** get_wifi_ssids()からのリストを返さなければなりませんか?代わりにシグナルが利用可能なときにリストを表示しないでください。それらを返す場合は、ネストされたイベントループを開始するか、スレッド全体でブロックするかのいずれかで、リストが利用可能になるまで何らかの形で関数内で停止する必要があります(これはさらに推奨されません)。 – Mike

+1

利用可能な場合は、リストで信号を発信することをお勧めします。あなたのクラスにリストを格納して、あなたのクラスからの最後の利用可能な結果を​​得るためにあなたのオブザーバーのためのシグナルを出してください(['QIODevice :: readyRead()'](https://doc.qt.io/qt -5/qiodevice.html#readyRead)シグナルの動作)。 – Mike

+0

@Mike Hmmそれは良い提案です。しかし、スレッドをブロックすることは悪い考えです(つまり、スキャンが完了するまでスピンロックを使用するなど)。私は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'は本番環境で使われることを意図したものではなく、より多くのハックです(このコメント(http:// stackoverflow。com/a/3556525/797744)を参照してください)。 – DBedrenko

+0

リンクされた回答のリンクはもう機能しませんが、私は彼らが次のような記事を指摘していると思います:http://delta.affinix.com/2006/10/23/nested-eventloops/安全な使用状況イベントループ。つまり、アプリケーションはどのように設計されていますか?オプションで、プロセスフラグを 'event.exec()'コール –

+0

に渡すこともできますが、私はTeemu Piippoに同意してください。ブロックフリーデザインを再考する。 –

5

スキャン操作が非同期であるので、最後までスキャンを待っていることは、ブロッキング操作ですので、あなたが本当に、SSIDをスキャンし、それらを返すメソッドを持つことはできません。ブロッキング操作により、イベントループが機能しなくなり、信号情報が処理されます。

get_wifi_ssidsメソッド内にローカルイベントループを設定できますが、これによりアプリケーションの残りの部分が機能しなくなります。 WiFiスキャンでハングアップが発生した場合、プログラムはその間にフリーズします。

代わりに、必要に応じてスキャンを開始するようにクラスを再設計し、get_wifi_ssidsはアクセスポイントに関する最新の情報を返します。

+1

アドバイスをいただきありがとうございます。しかし、そのデザインはどのように見えますか?私の質問のユースケースは、ユーザが私のアプリにSSIDとパスワードを使って特定のAPに接続するように要求したときです。接続しようとする前に、APをスキャンしてそのSSIDが存在するかどうかを確認し、そうでなければ要求を中止する必要があります。したがって、検査は終了するスキャンに依存します。 – DBedrenko

+0

ユーザがSSIDとパスワードを入力する前にアクセスポイントをスキャンしていない場合は、スキャンが完了してインターフェイスがロックアウトされているフェーズにプログラムを入力する必要があります。たとえば、SSIDとパスワードをキャッシュするように接続マネージャに指示し、スキャンが完了したらチェックをトリガーすることができます。 –

+0

インターフェイスは別のプロセスで実行されており、私のアプリケーションはDBusを介して通信しているので、ロックアウトする必要はありません(ブロックされているのは正しいですか?)。それでもユーザーが「接続」をクリックすることによって「スキャンフェーズ」が起動され、SSIDが存在するかどうかをチェックすることは、スキャンが終了したこと(最新の情報を取得する)に依存します。だから私はあなたの提案が何であるか分からない – DBedrenko

関連する問題