QNetworkAccessManager one instance and connecting slots
Asked Answered
T

1

1

I have my first Qt application in development. It's a desktop client for site messaging.

Qt documentation says that i need to have only one instance of QNetworkAccessManager accross application. But i also read that using of singletons with Qt isn't a good idea. How i can make one instance of QNetworkAccessManager across app?

Another question is how to properly connect slots while i call ApiHandler functions from other classes?

For example i have ExampleApi class which uses functions of ApiHandler, in updateMessageList slot messagesListUpdated should be connected after request finished to update message list in view, but it never called.

ExampleApi.h

...
#include "apihandler.h"

class ExampleApi : public QObject
{
    Q_OBJECT
public:
    explicit ExampleApi(QObject *parent = 0);
    void updateMessagesList();

signals:

public slots:
    void messagesListUpdated(QNetworkReply* reply);

};

ExampleApi.cpp

ExampleApi::ExampleApi(QObject *parent) :
    QObject(parent)
{
}

void ExampleApi::updateMessagesList()
{
    QMap<QString, QString> m;
    m["user_id"] = ConfigParser::settings.value(USER_ID).toString();

    QNetworkAccessManager nam;
    ApiHandler a(&nam);

    // Connect a slot
    connect(a.getManager(), SIGNAL(finished(QNetworkReply*)), this, SLOT(messagesListUpdated(QNetworkReply*)));

    a.makeRequest("messages.get", m);
}

void ExampleApi::messagesListUpdated(QNetworkReply* reply)
{
    QJsonDocument j = QJsonDocument::fromJson(reply->readAll());
    QJsonObject getjson = j.object();

    qDebug() << "ExampleApi::messagesListUpdated()" << getjson;
    reply->deleteLater();
    // ...
}

ApiHandler.h

class ApiHandler : public QObject
{
    Q_OBJECT

public:
    explicit ApiHandler(QNetworkAccessManager *nam, QObject *parent = 0);
    ~ApiHandler();
    QNetworkReply* makeRequest(QString method, QMap<QString, QString> parameters);
    QNetworkAccessManager* getManager();
private:
    QUrl buildCall(QString method, QMap<QString, QString> parameters);
    QNetworkAccessManager* manager;

signals:

public slots:
    void replyFinished(QNetworkReply* reply);
    void slotReadyRead();
    void slotError(QNetworkReply::NetworkError error);
    void slotSslErrors(QList<QSslError> errorList);
};

ApiHandler.cpp

#include "apihandler.h"

ApiHandler::ApiHandler(QNetworkAccessManager *nam, QObject *parent) :
    QObject(parent), manager(nam)
{
    Q_ASSERT(manager);
}

ApiHandler::~ApiHandler()
{
    manager->deleteLater();
}

QUrl ApiHandler::buildCall(QString method, QMap<QString, QString> parameters)
{
    QUrl url = QUrl(API_URL + method);
    QUrlQuery query;
    ConfigParser c;

    // Append first access_token
    query.addQueryItem("access_token", c.settings.value(ACCESS_TOKEN_KEY).toString());

    if (!parameters.isEmpty()) {
        QMapIterator<QString, QString> i(parameters);

        while (i.hasNext()) {
            i.next();
            query.addQueryItem(i.key(), i.value());
        }
    }

    url.setQuery(query.query());

    return url;
}

QNetworkReply* ApiHandler::makeRequest(QString method, QMap<QString, QString> parameters)
{
    QUrl url = this->buildCall(method, parameters);

    //qDebug() << "ApiHandler::makeRequest: " << url;

    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));

    QNetworkRequest request;

    request.setUrl(url);
    request.setRawHeader("User-Agent", "Site-Client");

    QNetworkReply *reply = manager->get(request);

    connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotError(QNetworkReply::NetworkError)));
    connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));

    return reply;
}

void ApiHandler::replyFinished(QNetworkReply* reply)
{
    qDebug() << "ApiHandler::replyFinished:" << reply->url();
    QJsonDocument j = QJsonDocument::fromJson(reply->readAll());
    if (j.isEmpty()) {
        // throw error
        qDebug("ApiHandler::replyFinished(...) j.isEmpty :(");
    } else {
        qDebug() << "ApiHandler::replyFinished(...)" << j;
    }

    reply->deleteLater();
}

void ApiHandler::slotReadyRead()
{
    //qDebug("slotReadyRead");
}

void ApiHandler::slotError(QNetworkReply::NetworkError error)
{
    qWarning() << "raised error:" << error;
}

void ApiHandler::slotSslErrors(QList<QSslError> errorList)
{
    qWarning() << "raised sslErrors" << errorList;
}

QNetworkAccessManager* ApiHandler::getManager()
{
    return this->manager;
}
Tsarina answered 5/9, 2017 at 13:55 Comment(2)
Your manager goes out of scope before the result is received.Precatory
The solution can be the same as with QCoreApplication and qApp: yes, it's a singleton, but it's a singleton whose lifetime you fully control.Stalinism
T
1

One instance of QNetworkAccessManager snippet

QNetworkAccessManager* getManager() {
    static QNetworkAccessManager* nam = new QNetworkAccessManager;
    return nam;
}
Tsarina answered 8/9, 2017 at 16:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.