如何使用异步源获取自定义小部件的数据

How to get data for a custom widget using an async source

我是 QT 的新手,我需要一些帮助。如果你们中有人能帮助我,我将不胜感激。

问题:

我有一个异步 class 发出 HTTP 请求,它将接收一些数据并转换成 JSON 格式,然后我将从那里提取应该传递给我的自定义的必要信息小部件。我怎样才能做到这一点?因为不知道信息什么时候到。

到目前为止我做了什么:

我的 HTTP 请求和解析 JSON class:

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 请求,并将数据放入必要的变量中。现在我的问题是......我如何才能等待接收到数据,然后再将它们放入这些变量中?

到目前为止,我所知道的唯一解决方案是使用 QEventLoop,但那时我基本上要将异步调用转换为同步调用,这不是我想要的。我想完全异步。

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

不,您不会在此处获取数据。 WeatherAPI::readData 是您获取数据的地方。

那就是Signal - Slot mechanism的重点了。您无需等待事件发生,而是通过回调(即插槽)对其做出反应。

考虑到这一点,您必须重新考虑并扩展您的代码。这是一种方法:

  1. WeatherAPIclass定义一个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 class 中定义一个具有以下实现的 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 的一个属性,因此它应该在 class.

的构造函数中初始化