QT – Code Snippet for QNetworkRequest Lambda Callback with Connect

  • infoFull Post Details
    info_outlineClick for Full Post Details
    Date Posted:
    Apr. 11, 2019
    Last Updated:
    Apr. 11, 2019
  • classTags
    classClick for Tags
    Tags:

This is not best practice, but I wanted to handle the result of a very simple web request (GET a URL) in as few lines of code as possible, primarily just as a test mechanism. Pretty much every example I could find out there either didn’t work, or required connecting true signal and slots, where the class that contains the initiating code would need to be a QObject derived class so that it can function as the slot mechanism for “receiving” the update. What was frustrating to me about this was that I have spent most of my coding time working with Javascript and similar web-oriented languages, which always have tons of options for asynchronous operations and things like callbacks, futures, and async/await.

Finally, after a little bit of searching and trail and error, I found the solution with Lambda expressions and the new syntax available for QObject::connect (which does not require that the receiver be a QObject slot):

 

void Helpers::checkInternetConnection(){

    qDebug() << "Checking internet connection...";
    QNetworkAccessManager *netManager = new QNetworkAccessManager();

    // Use lambda as receiver with connect to get result of network finished event
    QObject::connect(netManager,&QNetworkAccessManager::finished,[=](QNetworkReply *reply) {
        qDebug() << "In lambda!";
        if (reply->error()){
            qDebug() << "Fail";
        }
        else {
            qDebug() << "Pass!";
        }
    });

    // Start the network request
    QNetworkReply *netReply = netManager->get(QNetworkRequest(QString("https://www.google.com/")));

}

You can can even manipulate the value of outside variables through lambdas by including them in the capture argument. Here I’m de-referencing a pointer to a boolean argument and filling in the value based on the result of the network request:

// In Helpers class:
void Helpers::checkInternetConnection(bool *res){

    qDebug() << "Checking internet connection...";
    QNetworkAccessManager *netManager = new QNetworkAccessManager();

    // Use lambda as receiver with connect to get result of network finished event
    QObject::connect(netManager,&QNetworkAccessManager::finished,[=](QNetworkReply *reply) {
        qDebug() << "In lambda!";
        if (reply->error()){
            qDebug() << "Fail";
            *res = false;
        }
        else {
            qDebug() << "Pass!";
            *res = true;
        }
    });

    // Start the network request
    QNetworkReply *netReply = netManager->get(QNetworkRequest(QString("https://www.google.com/")));

}
// Elsewhere:
bool hasInternet = false;
Helpers::checkInternetConnection(&hasInternet);
QTimer::singleShot(2000,[&hasInternet](){
	qDebug () << "res = " << hasInternet;
});

Although this is tempting to use for a one-off testing mechanism, once I stopped to think for a moment about what I needed to do, I realized that I was better off not fighting using QObject classes. If I’m trying to track whether or not my application has internet access, I’m much better off creating a App Status QObject class, which would have a “hasInternet” member, which I can then hook together with a signal of “internetConnectionChanged” and have “hasInternet” be the receiver slot for my checkInternetConnection function.

Frankly, thanks to QT’s macro system, that actually isn’t even all that much boilerplate code, and once I set that up, I can then easily expose my QObject app status class to the QML side, and do something neat like fade out and disable buttons that rely on internet connected features when the application detects a loss of network.

Leave a Reply

Your email address will not be published. Required fields are marked *