Сделайте запрос на веб-службу, получите ответ json и обновите графический интерфейс в Qt

Пытаюсь изучить веб-службы с помощью Qt (используя Qt Creator 4.1.0) и подключить данные к графическому интерфейсу. Я прочитал несколько онлайн-примеров (в первую очередь: 1, 2 и 3), но мой низкий уровень кодирования вместе с тот факт, что я не смог найти полных примеров, демонстрирующих мои потребности, привел меня сюда :).

Я создал простой пример, содержащий все мои недостатки:

  • отправлять HTTP-запрос на получение к (существующей) веб-службе каждые 30 секунд.
  • Затем веб-служба отвечает, отправляя объект данных json (пример такого формата json см. ниже), который мы получаем и анализируем.
  • Затем Qt отобразит все проанализированные данные json в простом графическом интерфейсе (см. ниже, как выглядит такой графический интерфейс).

формат данных json — пример:

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

Мой простой дизайн графического интерфейса Qt (сделанный в Qt Creator), отображающий все извлеченные проанализированные данные: мой простой дизайн графического интерфейса Qt

Я был бы очень признателен за любой полный пример кода, который показывает, как мы можем сделать запрос к веб-службе, а затем как получить ответ json. Наконец, как подключить GUI в Qt для отображения этих данных по мере их поступления.

Я только начинаю изучать эту область, и мне нужен простой пример полного кода, чтобы начать работу.


person Community    schedule 15.09.2016    source источник
comment
Написали ли вы код для отправки HTTP-запроса на получение к (существующей) веб-службе каждые 30 секунд? вы показываете формат JSON и графический интерфейс, но не показываете код, который пробовали.   -  person Mike    schedule 16.09.2016
comment
@Mike Я следовал некоторым примерам с QNetworkAccessManager, которые я нашел, но отсутствие каких-либо полных руководств и примеров для моего начального уровня ни к чему не привело. Итак, у меня нет реального кода, просто несколько неудачных попыток фрагмента кода. Любое руководство и код для вышеуказанной проблемы помогут мне погрузиться в тему.   -  person    schedule 16.09.2016


Ответы (1)


Вот полностью рабочий пример того, как отправить запрос GET с параметрами в веб-службу с помощью QNetworkAccessManager и проанализируйте ответ JSON, используя QJsonDocument.

В примере я отправляю запрос http://uinames.com/, чьи ответы закодированы в JSON в следующем формате:

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

Я анализирую ответ JSON и отображаю его в графическом интерфейсе.

Пример скриншота

#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();
}

Редактировать:

Поскольку вы спрашивали, как использовать QTimer для запуска обновления каждые одну минуту, замените connect вызов кнопки clicked сигналом из кода выше на что-то вроде этого:

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

Как отмечено в комментариях, если вы используете это в конструкторе своего оконного класса, вы должны убедиться, что networkManager, networkRequest, компоненты графического интерфейса и timer здесь поддерживаются, пока ваш объект окна работает. Таким образом, вы можете разместить их в куче или в качестве членов класса.

person Mike    schedule 15.09.2016
comment
Это прекрасно, даже если не на моем примере, это ясно демонстрирует то, что я пытался понять. Спасибо. Небольшой вопрос: если у меня есть класс QMainWindow, значит ли это, что весь ваш код помещается в конструктор этого класса? - person ; 16.09.2016
comment
По той же теме, что и мой первый вопрос с QMainWindow, я пытался скопировать ваш код (с некоторыми изменениями) в классе, но получаю много ошибок; возможно, вы кратко объясните, как это должно работать? Спасибо - person ; 16.09.2016
comment
@nk-fford, у меня нет доступа к вашему веб-сервису, поэтому я написал общий пример, как вы просили, пример действительно работает так, как он есть. Нет, вы не копируете весь код в конструктор QMainWindow как есть. Основная проблема заключается в том, что networkManager, networkRequest и все компоненты графического интерфейса должны поддерживаться в рабочем состоянии, пока работает ваш QMainWindow. Вы можете разместить их в куче или в качестве членов класса. Кроме того, вы можете заменить лямбда-функции слотами. - person Mike; 16.09.2016
comment
Если вы используете дизайнер для своего графического интерфейса, компоненты графического интерфейса по умолчанию распределяются динамически, и вы можете получить к ним доступ с помощью указателя ui. Пример здесь демонстрирует использование QNetworkAccessManager и QJsonDocument. Проблемы, с которыми вы сталкиваетесь, помещая его в свой QMainWindow, относятся к общему языку C++. Причина, по которой я написал весь код в функции main, заключается в том, чтобы пример был как можно более минимальным и поддающимся проверке. - person Mike; 16.09.2016
comment
Я понял, спасибо за разъяснение. Последний вопрос: если бы я хотел, чтобы запрос на получение запускался каждую минуту, не могли бы вы показать, как это будет выглядеть в коде? Я запутался, как заменить кнопку. - person ; 16.09.2016
comment
Используйте QTimer и подключите его timeout() к слоту/лямбде, где инициируется запрос (это лямбда, подключенная к QPushButton::clicked в моем примере). - person Mike; 16.09.2016
comment
Я борюсь с таймером. Извините, я новичок во всем этом. Есть ли шанс предоставить небольшой фрагмент кода таймера, чтобы я мог увидеть его пример в исходном коде, пожалуйста? - person ; 16.09.2016
comment
@nk-fford, я обновил ответ. Хотя я думаю, что это следует задать как отдельный вопрос о QTimer, так как это не тесно связано с использованием QNetworkAccessManager и доступом к сети. сервис от Qt. - person Mike; 16.09.2016