Как получить данные для пользовательского виджета с использованием источника asyn c - PullRequest
0 голосов
/ 11 июля 2020

Я новичок в QT и мне нужна помощь. Если бы кто-нибудь из вас мог мне помочь, я был бы очень признателен.

ВОПРОС:

У меня есть асинхронный класс, который выполняет HTTP-запрос и будет получать некоторые данные в формат JSON, и оттуда я извлечу необходимую информацию, которая должна быть передана моему пользовательскому виджету. Как я могу это сделать? Потому что я не знаю, когда будет получена информация.

ЧТО Я ДЕЙСТВИТЕЛЬНО СДЕЛАЛ:

Мой HTTP-запрос и его анализ JSON класс:

WeatherAPI::WeatherAPI(QObject *parent) : QObject(parent) {
    manager = new QNetworkAccessManager(this);
    QObject::connect(manager, SIGNAL(finished(QNetworkReply * )), this, SLOT(readData(QNetworkReply * )));
}

void WeatherAPI::readData(QNetworkReply *reply) {
    if (reply->error() == QNetworkReply::NoError) {
        QString strReply = (QString) reply->readAll();
        QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8());
        QJsonObject jsonObject = jsonResponse.object();
        weatherObject.city = jsonObject["name"].toString();
        weatherObject.temperature = QString::number(jsonObject["main"].toObject()["temp"].toDouble() - 273.15);

        int ts = jsonObject["dt"].toInt();
        weatherObject.time = QDateTime::fromSecsSinceEpoch(ts).toString("hh:mm");
        auto weatherData = jsonObject["weather"].toArray()[0].toObject()["main"].toString();
        if (weatherData == "Clouds") {
            weatherObject.icon = "Sun.png";
        }
    } else {
        qDebug() << "ERROR";
    }
}

void WeatherAPI::requestDataForCity(const QString &city) {
    QString link = linkTemplate.arg(city, key);
    QUrl url(link);
    manager->get(QNetworkRequest(url));
}

const WeatherObject &WeatherAPI::getWeatherObject() const {
    return weatherObject;
}

Вот мой пользовательский виджет:

void WeatherButton::initStyle(const QJsonValue &json) {
    PolygonButtonWidget::initStyle(json);
    auto cities = json.toObject()["cities"].toArray();
    api = new WeatherAPI(this); 
    for (auto c: cities) {
        QString city = c.toString();
        api->requestDataForCity(city); // HERE I'm making the http request
        WeatherObject data = api->getWeatherObject();//HERE I'm getting the DATA
        m_title = data.city;
        m_time = data.time;
        m_icon = data.icon;
        m_temperature = data.temperature;
    }
}

В этой функции из WeatherButton :: initStyle Я собираюсь сделать HTTP-запрос, а также m собираюсь поместить данные в нужную переменную. Теперь мой вопрос ... Как я могу дождаться получения этих данных и сразу после этого поместить их в эти переменные?

Пока что единственное известное мне решение - использовать QEventL oop, но в этот момент я собираюсь в основном преобразовать вызов asyn c в вызов syn c, что не совсем то, что я хочу. Я хочу быть полностью асинхронным c.

1 Ответ

2 голосов
/ 12 июля 2020

WeatherObject data = api->getWeatherObject(); //HERE I'm getting the DATA

Нет, вы не получаете здесь данные. WeatherAPI::readData - это то место, где вы получаете данные.

Это суть механизма Сигнал - Слот . Вы не ждете, когда произойдет событие, а реагируете на него через обратный вызов, т.е. слот.

Имея это в виду, вы должны переосмыслить и расширить свой код. Вот один из способов сделать это:

  1. В классе WeatherAPI определите сигнал dataReady(const WeatherObject &weatherObject)

  2. Передайте этот сигнал в WeatherAPI::readData вот так:

     void WeatherAPI::readData(QNetworkReply *reply) {
         if (reply->error() == QNetworkReply::NoError) {
             // the processing of the http response remains unchanged
             // ...
             emit dataReady(weatherObject);
         } else {
             qDebug() << "ERROR";
         }
     }
    
  3. В классе WeatherButton определите слот onDataReady со следующей реализацией:

     void WeatherButton::onDataReady(const WeatherObject &weatherObject) {
         m_title = weatherObject.city;
         m_time = weatherObject.time;
         m_icon = weatherObject.icon;
         m_temperature = weatherObject.temperature;
     }
    
  4. Подключите вновь созданный сигнал и слот в WeatherButton::initStyle следующим образом:

     void WeatherButton::initStyle(const QJsonValue &json) {
         PolygonButtonWidget::initStyle(json);
         auto cities = json.toObject()["cities"].toArray();
         api = new WeatherAPI(this);
         connect(api, &WeatherAPI::dataReady, this, &WeatherButton::onDataReady);
    
         for (auto c: cities) {
             QString city = c.toString();
             api->requestDataForCity(city); // HERE I'm making the http request
         }
     }
    

В качестве примечания я должен сказать, что initStyle, вероятно, не лучшее место для создания экземпляра WeatherAPI. api кажется атрибутом WeatherButton, поэтому он должен быть инициализирован в конструкторе класса.

...