Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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



  • I'm new to QT and I would like some help. If any of you could help me I would really appreciate it.

    QUESTION:

    I have an asynchronous class that makes an HTTP request and it's going to receive some data into a JSON format and from there I will extract the necessary information which should be passed to my custom widget. How can I do that? Because I don't know when the information will arrive.

    WHAT I'VE DONE SO FAR:

    My HTTP request and parsing 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;
    }
    

    Now here is my custom Widget:

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

    In that function from WeatherButton::initStyle I'm going to make an HTTP request and also I'm going to place the data into the necessary variable. Now my question is... How can I wait for that data to be received and just after that to place them into those variables?

    So far the only solution I know so far is to use a QEventLoop, but at that moment I'm going to basically convert an async call into a sync one, which is not quite what I want. I want to be fully async.



  • @Vildnex said in Qt - CPP - How to get data for a custom widget using an async source:

    but at that moment I'm going to basically convert an async call into a sync one, which is not quite what I want. I want to be fully async.

    What do you mean by "fully async"? :) initStyle() is called synchronously from code, so yes it requires a blocking call here. Or, you have to figure a way to maybe redraw your custom weather widget as & when the asynchronous data arrives, which is presumably the best you can hope to achieve?

    As a separate issue/on top of that, perhaps you should consider caching the HTTP data for future use, e.g. in this method?


  • Lifetime Qt Champion

    Hi,

    From your WeatherAPI, you should emit a signal that will propagate the data received and connect that signal to whatever slot you need to update your widget.

    Out of curiosity, why are you using the old syntax with a lambda when you could directly connect the slot ?


Log in to reply