QNetworkAccessManager doesn't emit finished without blocking current thread



  • in some of my methods:
    @connect(nwam, SIGNAL(finished(QNetworkReply*)) , this, SLOT(processReply(QNetworkReply*)) );@

    processReply is @QByteArray ba = r->readAll();
    qDebug() << ba;@

    finished signal emits only if i do
    @
    QEventLoop eventLoop;
    connect(nwam, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
    eventLoop.exec();
    @
    But it will block main thread.So, what I am doing wrong?



  • How do you send a request?
    Is it one of http requests or custom request?


  • Moderators

    Hi, and welcome to the Qt Dev Net!

    Which thread does your QNAM live in?

    Which thread runs processReply?

    How did you create + start your threads?



  • nwam->get(*request);
    [quote author="andreyc" date="1396197278"]How do you send a request?
    Is it one of http requests or custom request?[/quote]



  • i don't use my own threads, i do all the things inside main thread because , as i can see, QNAM is async by design
    [quote author="JKSH" date="1396218189"]Hi, and welcome to the Qt Dev Net!

    Which thread does your QNAM live in?

    Which thread runs processReply?

    How did you create + start your threads?[/quote]


  • Moderators

    QNAM itself is async, but signal delivery relies on a working event loop.

    Qt is event driven, so you should be running all your code inside an event loop, especially if you want to receive events and fire slots.
    Don't think in terms "my program runs code from top to bottom but hangs at exec(). You should rather think "my program spins inside exec() and notifies me via slots and event handlers when something happens".


  • Moderators

    To add to Chris: Your main thread needs a running event loop in order for QNAM to work. That means you must call QCoreApplication::exec() -- or else no signals and slots will be processed.



  • So, do you mean that i have to write something like this?
    @
    nwam->get(request);
    connect(nwam, SIGNAL(finished(QNetworkReply
    )) , this, SLOT(processTaskListsReply(QNetworkReply*)) );

    QEventLoop eventLoop;
    connect(nwam, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
    eventLoop.exec();
    @



  • Why do you use your own QEventLoop anyways? Don't you have a QCoreApplication running in your program?



  • I have typical main.cpp in my project
    @
    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
    }
    @
    And i have separate class, which use QNAM



  • then you should not need a second QEventLoop (QApplication already has a QEventLoop internally), that might be the problem why your signals and slots are not working as they should.
    The QApplication is usually a global object in your application, you can access it with the global pointer qApp from anywhere if you need to.

    So it should not matter if you create the QNAM from your Dialog or in the main.cpp or whatever, you don't need to create a QEventLoop yourself for it to work.



  • well, then .. why it does not work properly? If i use my QEventloop i see that it freeze a little bit but it works


  • Moderators

    Hard to tell with the information you gave. Maybe you're deleting the receiving object before the finish signal fires? And since you put additional blocking exec() it works.
    Some more context and code would be helpful.



  • @
    QNetworkAccessManager *nwam = new QNetworkAccessManager();
    QNetworkRequest request = new QNetworkRequest(QUrl(listUrl));
    request->setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    nwam->get(request);
    connect(nwam, SIGNAL(finished(QNetworkReply
    )) , this, SLOT(processTaskListsReply(QNetworkReply
    )) );

    @
    @
    processTaskListsReply(QNetworkReply *r)
    {
    qDebug() << "processTaskListsReply();";
    QByteArray ba = r->readAll();
    qDebug() << ba;
    }
    @


  • Moderators

    I meant the context of it. Where is it created. What is the lifetime of the object it holds these. Where is the nwam deleted etc.



  • it almost looks like you create a new QNetworkAccessManager for every request? hard to tell from your snippet but that is not a very good idea.

    If you read the doc of QNAM it says one object per app should be sufficient in most cases, so usually you have one global QNAM and there is no need to create it on the heap (with new).

    What I would do, if you onyl have one Dialog in your app then just create an object in there:
    @
    class Dialog : public QDialog
    {
    Q_OBJECT
    private:
    QNetworkAccessManager m_networkManager;
    };
    @
    and then connect the signals of m_networkManager in the constructor of the Dialog only once and reuse the QNAM for all requests. Unless you only need the QNAM in rare cases this would be a better way I think.



  • ou, i see: i have to use only the one QNAM for all classes of my app. Thanks, i will try it later



  • You don't have to use only one instance, but it would be considered best practice to only have one, because the QNAM initialization might be expensive and it also caches many things like DNS resolution etc, if you create a new one every time that cache will be lost and network requests take longer.
    There was a nice talk about it, if you want take a look I found it very interesting. "QtDD13 - Peter Hartmann - Speeding up your Qt app with new QtNetwork features":https://www.youtube.com/watch?v=OM9BgcXr9ys&list=LLa69qk-_WIRna0JNQf8Md0w.



  • ou, thanks, i will try to watch all that videos if i will get time. I will use Singleton



  • Singleton may be not so good idea, i found this

    @
    DbHandler* dbHandler = new DbHandler;

    // Make your DbHandler* globally accessible by settings it as a
    // dynamic property of the application object.
    
    application.setProperty("dbHandler", QVariant::fromValue<DbHandler*>(dbHandler));
    
    ....
    

    }

    void SomeObject::someMethod()
    {
    DbHandler* dbHandler = qApp->property("dbHandler").value<DbHandler*>();
    }
    @

    But... what if i wand to split a part of my app as library later, then i will get dependency injection: users will have to give QNAM instance to my constructor, is it right way?


  • Moderators

    [quote author="blokant" date="1396679021"]Singleton may be not so good idea[/quote]Why not?

    [quote]But... what if i wand to split a part of my app as library later, then i will get dependency injection: users will have to give QNAM instance to my constructor, is it right way?[/quote]Your library should be the one that creates the QNAM. Users of your library shouldn't need to know that you are using a QNAM behind the scenes.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.