2016-11-22 12 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' не предназначены для использования в производстве и являются более взломанными (см. Комментарии к этому ([ответ] (http: // stackoverflow.com/a/3556525/797744) для получения дополнительной информации). – DBedrenko

+0

Ссылки в связанном ответе больше не работают, но я думаю, что они указали на некоторые статьи следующим образом: http://delta.affinix.com/2006/10/23/nested-eventloops/ Там _are_ безопасные ситуации для использования цикл событий. Итак, вопрос в том, как ваше приложение разработано? При желании вы также можете передавать флаги процессов на вызов 'event.exec()' –

+0

. Но я согласен с Teemu Piippo в том, что приложение GUI не должно блокироваться в таком случае. Пересмотрите, чтобы создать бесквартирный дизайн. –

5

Поскольку операция сканирования является асинхронным, вы не можете действительно есть метод, который сканирует SSIDs и возвращает их, потому что ждет сканирования, чтобы закончить это блокирующая операция , Операции блокировки препятствуют работе цикла событий и обработке информации о сигналах.

Вы можете иметь локальный цикл событий внутри метода get_wifi_ssids, но это заблокирует работу остальной части приложения. Если при сканировании WiFi произойдет зависание, программа замерзнет во время него.

Вместо этого перепроектируйте класс так, чтобы он начинал сканирование, когда это необходимо, и get_wifi_ssids возвращает обновленную информацию о точках доступа.

+1

Спасибо за совет. Но как будет выглядеть этот дизайн? Вариант использования в моем вопросе заключается в том, когда пользователь запрашивает мое приложение для подключения к определенной точке доступа с SSID и паролем. Прежде чем попытаться подключиться, я должен выполнить проверку на наличие точек доступа и проверить, существует ли этот SSID, в противном случае прервать запрос. Таким образом, проверка зависит от завершения сканирования. – DBedrenko

+0

Если вы не просмотрели точки доступа до того, как пользователь предоставит SSID и пароль, программе необходимо ввести фазу, в которой выполняется сканирование, и интерфейс заблокирован. Например, вы можете сообщить диспетчеру подключения кэшировать SSID и пароль и запустить проверку при выполнении сканирования. –

+0

Интерфейс работает в другом процессе, и мое приложение общается с ним через DBus, поэтому его не нужно блокировать (вы имеете в виду заблокированный правый?). Даже тогда «фаза сканирования» запускается пользователем, нажимая «Connect», и проверка наличия SSID по-прежнему зависит от завершения сканирования (для получения последней информации). Поэтому я не уверен, что ваше предложение – DBedrenko