2016-11-22 2 views
3

Ich habe eine Klasse ConnectionManager mit Methode get_wifi_ssids(), die eine Liste von SSIDs zurückgeben muss. Das Problem ist, dass man diese SSIDs braucht, um Signale und Slots zu bekommen, aber ich kann keinen Weg finden, diese Informationen zu erhalten, ohne die Methode zu beenden.Wie eine Methode ein Signal senden und warten auf den Steckplatz vor der Rückkehr?

Hier ist die Hierarchie der Klassen von der niedrigsten bis zur höchsten Ebene.

/** 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; 
} 

Die Gesamtheit meiner Anwendung ist in einem einzigen Thread. "connman" wird von WifiConroller über DBus befohlen, und es ist ein separater Prozess, so offensichtlich in einem separaten Thread. Die GUI läuft in einem separaten Prozess und meine App kommuniziert mit ihr über DBus.

Eine QEventLoop ist eine schlechte Lösung, weil es nicht in der Produktion verwendet werden soll und eher ein Hack ist, so die Kommentare in this answer.

+1

Müssen Sie die Liste wirklich von ** get_wifi_ssids() '** zurückgeben? warum nicht die Liste in einem Signal ausstrahlen, wenn sie stattdessen verfügbar sind? Wenn Sie sie zurückgeben wollen, müssen Sie irgendwie innerhalb der Funktion anhalten, bis die Liste verfügbar ist, indem Sie entweder eine verschachtelte Ereignisschleife starten (und davon abgeraten wird) oder durch den ganzen Thread blockieren (und das wird noch mehr entmutigt)). – Mike

+1

Ich würde vorschlagen, ein Signal mit der Liste zu senden, wenn es verfügbar ist. Oder speichern Sie die Liste in Ihrer Klasse und geben Sie Ihrem Beobachter ein Signal, um das letzte verfügbare Ergebnis aus Ihrer Klasse zu erhalten (etwa wie ['QIODevice :: readyRead()'] (https://doc.qt.io/qt -5/qiodevice.html # readyRead) Signal funktioniert). – Mike

+0

@Mike Hmm das ist ein guter Vorschlag. Aber wieso sagen Sie, dass das Blockieren des Threads eine schlechte Idee ist (d. H. Einen Spinlock verwenden, um zu warten, bis der Scan beendet ist)? Ich habe kein GUI-Thread (es läuft in einem separaten Prozess und meine App spricht über DBus). – DBedrenko

Antwort

2

Sie können ein mit lokaler 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

Vielen Dank für Ihre Hilfe, aber 'QEventLoop's sind nicht für die Produktion gedacht und sind eher ein Hack (siehe Kommentare hierzu ([Antwort] (http: // stackoverflow.com/a/3556525/797744) für weitere Informationen). – DBedrenko

+0

Die Links in der verknüpften Antwort funktionieren nicht mehr, aber ich denke, sie haben auf einen Artikel wie diesen hingewiesen: http://delta.affinix.com/2006/10/23/nested-eventloops/ Dort sind _ sichere Situationen zu verwenden eine Ereignisschleife. Die Frage ist also, wie ist Ihre Anwendung konzipiert? Optional können Sie Prozess-Flags auch an den Aufruf 'event.exec()' übergeben. –

+0

Aber ich stimme mit Teemu Piippo überein, dass eine GUI-Anwendung in einem solchen Fall nicht blockieren sollte. Überdenken Sie, um ein Block-freies Design zu machen. –

5

Da der Scanvorgang asynchron ist, kann man nicht wirklich eine Methode, die die SSIDs scannt und gibt ihr, weil für den Scan wartet ein Sperrvorgang beendet ist . Blockierungsoperationen verhindern, dass die Ereignisschleife funktioniert, und signalisieren Informationen, um verarbeitet zu werden.

Sie können eine lokale Ereignisschleife innerhalb der get_wifi_ssids-Methode verwenden, dies blockiert jedoch den Rest der Anwendung. Wenn der WLAN-Scan unterbrochen wird, wird das Programm währenddessen eingefroren.

Konfigurieren Sie stattdessen die Klasse so, dass sie den Scan bei Bedarf startet, und get_wifi_ssids gibt die aktuellen Informationen zu den Access Points zurück.

+1

Danke für den Rat. Aber wie würde dieses Design aussehen? Der Anwendungsfall in meiner Frage ist, wenn der Benutzer meine App anfordert, eine Verbindung zu einem bestimmten AP mit SSID und Passwort herzustellen. Bevor ich versuche, eine Verbindung herzustellen, muss ich nach APs suchen und prüfen, ob diese SSID vorhanden ist, ansonsten die Anfrage abbrechen. Die Überprüfung hängt also davon ab, dass der Scan abgeschlossen ist. – DBedrenko

+0

Wenn Sie die Zugriffspunkte nicht gescannt haben, bevor der Benutzer die SSID und das Kennwort eingegeben hat, muss das Programm eine Phase eingeben, in der der Scan durchgeführt wird und die Schnittstelle gesperrt ist. Zum Beispiel können Sie den Verbindungsmanager anweisen, die SSID und das Passwort zwischenzuspeichern und die Überprüfung auszulösen, wenn der Scan abgeschlossen ist. –

+0

Die Schnittstelle läuft in einem anderen Prozess, und meine App kommuniziert mit ihr über DBus, so dass sie nicht gesperrt werden muss (Sie meinen blockiert, oder?). Selbst dann wird die "Scan-Phase" ausgelöst, wenn der Benutzer auf "Verbinden" klickt und die Überprüfung, ob die SSID vorhanden ist, hängt noch davon ab, dass der Scan abgeschlossen ist (um die neuesten Informationen zu erhalten). Also ich bin mir nicht sicher, was dein Vorschlag ist – DBedrenko

Verwandte Themen