3

Trying to learn Web Services with Qt (using Qt Creator 4.1.0) and connecting data to the GUI. I have read several online examples (most notably: 1, 2 and 3) but my low coding level along with the fact that I could not find full examples that were demonstrating my needs lead me here :).

I have created a simple example so that contains all my shortcomings:

  • make an HTTP get request to a (existing) web service every 30 seconds.
  • The web service then responds by sending a json data object (see below for such a json example format) which we receive and parse.
  • Then, the Qt will display all the parsed json data onto a simple GUI (see below for how such a GUI looks like.

json data format - example:

{
    "city": "London",
    "time": "16:42",
    "unit_data": 
        [
            {
                "unit_data_id": "ABC123",
                "unit_data_number": "21"
            }
        ]
}

My simple Qt GUI design (made in Qt Creator) displaying all the fetched parsed data: my simple Qt GUI design

I would really appreciate any full code example that show how we can make a request to a web service and then how to fetch the json response. Finally, how to connect the GUI in Qt to display this data as soon as they are received.

I am just starting studying this area and I am in need of a simple full code example to get me going.

  • Have you written the code to *make the HTTP get request to the (existing) web service every 30 seconds*? you show the JSON format and the GUI, but you don't show any code you've tried. – Mike Sep 15 '16 at 21:29
  • @Mike I followed some examples with `QNetworkAccessManager` I found, but the lack of any full tutorials and examples for my entry level led to nothing. So, I have no actual code, just several failed code chunk attempts. Any guide and code for the above problem will help me dive into the subject. –  Sep 15 '16 at 21:35

1 Answers1

3

Here is a fully working example on how to send a GET request with parameters to a web service using QNetworkAccessManager and parse the JSON response using QJsonDocument.

In the example, I am sending a request to http://uinames.com/ whose responses are encoded in JSON in the following format:

{
    "name":"John",
    "surname":"Doe",
    "gender":"male",
    "region":"United States"
}

I am parsing the JSON response and displaying it in a GUI.

Example screenshot

#include <QtWidgets>
#include <QtNetwork>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //setup GUI (you could be doing this in the designer)
    QWidget widget;
    QFormLayout layout(&widget);
    QLineEdit lineEditName;
    QLineEdit lineEditGender;
    QLineEdit lineEditRegion;
    auto edits = {&lineEditName, &lineEditGender, &lineEditRegion};
    for(auto edit : edits) edit->setReadOnly(true);
    layout.addRow("Name:", &lineEditName);
    layout.addRow("Gender:", &lineEditGender);
    layout.addRow("Region:", &lineEditRegion);
    QPushButton button("Get Name");
    layout.addRow(&button);

    //send request to uinames API
    QNetworkAccessManager networkManager;
    QObject::connect(&networkManager, &QNetworkAccessManager::finished,
                     [&](QNetworkReply* reply){
        //this lambda is called when the reply is received
        //it can be a slot in your GUI window class
        //check for errors
        if(reply->error() != QNetworkReply::NoError){
            for(auto edit : edits) edit->setText("Error");
            networkManager.clearAccessCache();
        } else {
            //parse the reply JSON and display result in the UI
            QJsonObject jsonObject= QJsonDocument::fromJson(reply->readAll()).object();
            QString fullName= jsonObject["name"].toString();
            fullName.append(" ");
            fullName.append(jsonObject["surname"].toString());
            lineEditName.setText(fullName);
            lineEditGender.setText(jsonObject["gender"].toString());
            lineEditRegion.setText(jsonObject["region"].toString());
        }
        button.setEnabled(true);
        reply->deleteLater();
    });
    //url parameters
    QUrlQuery query;
    query.addQueryItem("amount", "1");
    query.addQueryItem("region", "United States");
    QUrl url("http://uinames.com/api/");
    url.setQuery(query);
    QNetworkRequest networkRequest(url);
    //send GET request when the button is clicked
    QObject::connect(&button, &QPushButton::clicked, [&](){
        networkManager.get(networkRequest);
        button.setEnabled(false);
        for(auto edit : edits) edit->setText("Loading. . .");
    });

    widget.show();
    return a.exec();
}

Edit:

Since you asked about how to use a QTimer to trigger the update every one minute, replace the connect call of the button clicked signal from the code above with something like this:

QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&](){
    networkManager.get(networkRequest);
    button.setEnabled(false);
    for(auto edit : edits) edit->setText("Loading. . .");
});
timer.start(60000); //60000 msecs = 60 secs

As noted in comments, if you are using this in your window class's constructor, you have to make sure that the networkManager, networkRequest, the GUI components, and the timer here are kept alive as long as your window object is running. So, you may choose to allocate them in the heap or as class members.

Mike
  • 8,055
  • 1
  • 30
  • 44
  • That is perfect, even if not with my example, it clearly demonstrates what I was seeking to understand. Thanks. Small question, if I have a `QMainWindow` class does that mean that all your code into the constructor of that class? –  Sep 16 '16 at 05:25
  • On the same topic as my first question with `QMainWindow`, I tried to copy your code (with some modifications) it in the class but I get many errors; perhaps you would briefly explain how that should work? Thanks –  Sep 16 '16 at 05:44
  • @nk-fford , I don't have access to your web service that's why I wrote a general example as you asked, the example is indeed working as it it. No, you don't copy all the code to `QMainWindow`'s constructor as it is. The main issue is that `networkManager`, `networkRequest`, and all GUI components need to be kept alive as long as your `QMainWindow` is running. You may choose to allocate them on the heap or as class members. Also, you may want to replace the lambda functions with slots. – Mike Sep 16 '16 at 08:27
  • If you are using the designer for your GUI, the GUI components are allocated dynamically by default, and you can access them using the `ui` pointer. The example here demonstrates usage of the `QNetworkAccessManager` and the `QJsonDocument`. Problems you are getting when putting it in your `QMainWindow` are of general C++ language. The reason I wrote all the code in `main` function is for the example to be as minimum and verifiable as possible. – Mike Sep 16 '16 at 08:34
  • I understand, thanks for the clarification. Last question: if I wanted to have the get request triggered every 1 minute could you please show how that would look in the code? I get confused with how to replace the button. –  Sep 16 '16 at 08:53
  • Use a [`QTimer`](https://doc.qt.io/qt-5/qtimer.html#details), and connect its [`timeout()`](https://doc.qt.io/qt-5/qtimer.html#timeout) signal, to the slot/lambda where the request is initiated (this is the lambda connected to `QPushButton::clicked` in my example). – Mike Sep 16 '16 at 09:07
  • I struggle with the timer. Sorry, I am a newbie with all this. Any chance to provide a small chunk of code of the timer so I can see an example of it in your original code please? –  Sep 16 '16 at 09:43
  • 1
    @nk-fford , I have updated the answer. Although I think this should be asked as a [separate question](https://meta.stackexchange.com/a/156902) about `QTimer` as this is not closely related to using `QNetworkAccessManager` and accessing a web service from Qt. – Mike Sep 16 '16 at 11:49