Using QNetwork and QEventLoop, not working signals.
-
wrote on 29 Jul 2022, 20:40 last edited by
Hi all,
I have a simple example of the https request:
void sendRequest(); QNetworkAccessManager mgr; int main(int argc, char *argv[]) { QCoreApplication *a = new QCoreApplication (argc, argv); QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), a, SLOT(quit())); sendRequest(); return a->exec(); } void sendRequest(){ // create custom temporary event loop on stack QEventLoop eventLoop; // "quit()" the event-loop, when the network request "finished()" QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit())); // the HTTP request QNetworkRequest req( QUrl( QString("http://ip.jsontest.com/") ) ); QNetworkReply *reply = mgr.get(req); qDebug() << "Is finished? " <<reply->isFinished(); eventLoop.exec(); // blocks stack until "finished()" has been called if (reply->error() == QNetworkReply::NoError) { //success qDebug() << "Is finished? " <<reply->isFinished(); qDebug() << "Success" <<reply->readAll(); qDebug() << "Is finished? " <<reply->isFinished(); delete reply; } else { //failure qDebug() << "Failure" <<reply->errorString(); delete reply; } }
This is the console output:
Is finished? false Is finished? true Success "{\"ip\": \"2a02:a314:8445:9080:9c00:7de2:c742:277f\"}\n" Is finished? true
I have read lot of topics how to quit the application correctly and it seems that using quit() function is correct but I can't make it work. If starting the code in terminal I have to close it manually, same when running directly in QTCreator, I have to click on "Stop running program". How to solve this?
Besides, when looking at output, before executingreply->readAll();
, reply is already finished, so it looks my signal->slot connection to quit eventLoop doesn't work? What can be the problem?
// Ok I think I've answered my second question when reading this: https://dekonvoluted.github.io/programming/2018/09/16/qt-event-loop.html
In my casevoid sendRequest()
has to finish and then control goes back to main loop isn't it?
However how to quit/exit main eventloop and stop the program? -
wrote on 30 Jul 2022, 11:15 last edited by Jacobotto 8 Sept 2022, 19:58
Yes, I did clean, run qmake again and rebuild project. I'm using QtCreator on Windows 10, Qt6.2.3, MSVC2019 compiler, x64.
//EDIT - From what I read it can be an issue with .moc file, which is required when using QOBJECT macro with signals/slots and so on, I see it's not created, there are only these files in the build dir:
.. and inside "debug":
//EDIT2 - Solution: It is necessary to include .moc file (but I don't know how to generate it etc.) OR just declare QObject in the header file what I obviously did not <facepalm>, and every other declarations too! Lesson for every beginner, use .cpp and .h files, do not put everything in just .cpp file/s. Now moc files are generated automatically and the program works!
Second thing, no need to use eventloops but if somebody wants to (and how to quit them..): https://stackoverflow.com/questions/4180394/how-do-i-create-a-simple-qt-console-application-in-c
Regarding to my first post, I found it can be done better without unnatural eventloop:
"(...)get function is asynchronous, so at the point you are trying to read the answer the http request might not even be completed yet.
To solve this, you need to handle the finished signal:"connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
"and read the results in the handler:"
void NetworkHandler::replyFinished(QNetworkReply *reply) { qDebug() << reply->readAll(); }
Source: https://stackoverflow.com/questions/20441756/qt-how-can-i-send-https-request-to-server
//EDIT3
One more thing, as comment from above source says:
"you need an event loop to use QNetworkAccessManager. If your code does not run inside a QCoreApplication::exec() event loop then you're going to have to use the QEventLoop class, but be warned that it is considered to be sloppy code. –"So I want to present my final code for this if anybody is interested:
header file:#ifndef HEADER_NAME_H #define HEADER_NAME_H #pragma once #include <QCoreApplication> #include <QDebug> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QUrl> #include <QUrlQuery> #endif // HEADER_NAME_H using json = nlohmann::json; class Foo : public QObject { Q_OBJECT public: Foo( QObject* parent = 0 ) : QObject( parent ) { sendRequest(); } private: QNetworkAccessManager mgr; QNetworkReply *reply; void sendRequest(); signals: void finished(); private slots: void replyFinished(QNetworkReply *reply); };
main cpp file:
#include <header.h> void Foo::replyFinished(QNetworkReply *reply) { qDebug() << reply->readAll(); emit finished(); }; void Foo::sendRequest() { QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); // the HTTP request QNetworkRequest req( QUrl( QString("http://ip.jsontest.com/") ) ); mgr.get(req); }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Foo foo; QObject::connect( &foo, &Foo::finished, &a, &QCoreApplication::quit ); return a.exec(); }
Now all works fine and app quits properly with code 0.
Another interesting source I was basing on, it's about downloading the file but it doesn't matter, it's about almost the same:
https://www.bogotobogo.com/Qt/Qt5_Downloading_Files_QNetworkAccessManager_QNetworkRequest.php -
Hi all,
I have a simple example of the https request:
void sendRequest(); QNetworkAccessManager mgr; int main(int argc, char *argv[]) { QCoreApplication *a = new QCoreApplication (argc, argv); QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), a, SLOT(quit())); sendRequest(); return a->exec(); } void sendRequest(){ // create custom temporary event loop on stack QEventLoop eventLoop; // "quit()" the event-loop, when the network request "finished()" QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit())); // the HTTP request QNetworkRequest req( QUrl( QString("http://ip.jsontest.com/") ) ); QNetworkReply *reply = mgr.get(req); qDebug() << "Is finished? " <<reply->isFinished(); eventLoop.exec(); // blocks stack until "finished()" has been called if (reply->error() == QNetworkReply::NoError) { //success qDebug() << "Is finished? " <<reply->isFinished(); qDebug() << "Success" <<reply->readAll(); qDebug() << "Is finished? " <<reply->isFinished(); delete reply; } else { //failure qDebug() << "Failure" <<reply->errorString(); delete reply; } }
This is the console output:
Is finished? false Is finished? true Success "{\"ip\": \"2a02:a314:8445:9080:9c00:7de2:c742:277f\"}\n" Is finished? true
I have read lot of topics how to quit the application correctly and it seems that using quit() function is correct but I can't make it work. If starting the code in terminal I have to close it manually, same when running directly in QTCreator, I have to click on "Stop running program". How to solve this?
Besides, when looking at output, before executingreply->readAll();
, reply is already finished, so it looks my signal->slot connection to quit eventLoop doesn't work? What can be the problem?
// Ok I think I've answered my second question when reading this: https://dekonvoluted.github.io/programming/2018/09/16/qt-event-loop.html
In my casevoid sendRequest()
has to finish and then control goes back to main loop isn't it?
However how to quit/exit main eventloop and stop the program?wrote on 29 Jul 2022, 20:53 last edited by@Jacobotto said in Using QNetwork and QEventLoop, not working signals.:
However how to quit/exit main eventloop and stop the program?
Call
QCoreApplication::quit()
. That will cause return froma->exec()
. -
wrote on 30 Jul 2022, 09:25 last edited by
Sorry but how to achieve that? I was trying to call it just at the end of my function and put into signal/slot but for nothing.
Then I tried to rebuild it a bit and create QObject like that:#include <QCoreApplication> #include <QDebug> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QUrl> #include <QUrlQuery> #include <QObject> class Foo : public QObject { Q_OBJECT public: Foo( QObject* parent = 0 ) : QObject( parent ) {sendRequest();} private: void sendRequest() { // create custom temporary event loop on stack QEventLoop eventLoop; // "quit()" the event-loop, when the network request "finished()" QNetworkAccessManager mgr; QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit())); // the HTTP request QNetworkRequest req( QUrl( QString("http://ip.jsontest.com/") ) ); QNetworkReply *reply = mgr.get(req); eventLoop.exec(); // blocks stack until "finished()" has been called if (reply->error() == QNetworkReply::NoError) { //success qDebug() << "Success" <<reply->readAll(); delete reply; } else { //failure qDebug() << "Failure" <<reply->errorString(); delete reply; } emit finished(); } signals: void finished(); }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Foo foo; QObject::connect( &foo, &Foo::finished, &a, &QCoreApplication::quit ); return a.exec(); }
However I'm getting 5x LNKxxx errors. Any tips?
My .pro file:QT += core QT -= gui QT += network CONFIG += console CONFIG -= app_bundle TEMPLATE = app CONFIG += c++11 console CONFIG -= app_bundle # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
-
Sorry but how to achieve that? I was trying to call it just at the end of my function and put into signal/slot but for nothing.
Then I tried to rebuild it a bit and create QObject like that:#include <QCoreApplication> #include <QDebug> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QUrl> #include <QUrlQuery> #include <QObject> class Foo : public QObject { Q_OBJECT public: Foo( QObject* parent = 0 ) : QObject( parent ) {sendRequest();} private: void sendRequest() { // create custom temporary event loop on stack QEventLoop eventLoop; // "quit()" the event-loop, when the network request "finished()" QNetworkAccessManager mgr; QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit())); // the HTTP request QNetworkRequest req( QUrl( QString("http://ip.jsontest.com/") ) ); QNetworkReply *reply = mgr.get(req); eventLoop.exec(); // blocks stack until "finished()" has been called if (reply->error() == QNetworkReply::NoError) { //success qDebug() << "Success" <<reply->readAll(); delete reply; } else { //failure qDebug() << "Failure" <<reply->errorString(); delete reply; } emit finished(); } signals: void finished(); }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Foo foo; QObject::connect( &foo, &Foo::finished, &a, &QCoreApplication::quit ); return a.exec(); }
However I'm getting 5x LNKxxx errors. Any tips?
My .pro file:QT += core QT -= gui QT += network CONFIG += console CONFIG -= app_bundle TEMPLATE = app CONFIG += c++11 console CONFIG -= app_bundle # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
wrote on 30 Jul 2022, 10:03 last edited by JonB@Jacobotto said in Using QNetwork and QEventLoop, not working signals.:
I was trying to call it just at the end of my function and put into signal/slot but for nothing.
So far as I know, just calling
QCoreApplication::quit()
should do it. Why don't you test it by commenting out all the code insendRequest()
and just goingQCoreApplication::quit()
? (Maybe that has no effect if called beforea->exec()
, I don't know.)Meanwhile, so long as you have a local event loop in
sendRequest()
and all you want to do is issue that one request and quit your program, it seems to me you might as well remove your call toa->exec()
completely, thenmain()
will exit whensendRequest()
returns, which is what you seem to want.However I'm getting 5x LNKxxx errors. Any tips?
Yes. How about pasting these so I/we don't have to guess what they might be referring to if you want us to say what is wrong?
-
@Jacobotto said in Using QNetwork and QEventLoop, not working signals.:
I was trying to call it just at the end of my function and put into signal/slot but for nothing.
So far as I know, just calling
QCoreApplication::quit()
should do it. Why don't you test it by commenting out all the code insendRequest()
and just goingQCoreApplication::quit()
? (Maybe that has no effect if called beforea->exec()
, I don't know.)Meanwhile, so long as you have a local event loop in
sendRequest()
and all you want to do is issue that one request and quit your program, it seems to me you might as well remove your call toa->exec()
completely, thenmain()
will exit whensendRequest()
returns, which is what you seem to want.However I'm getting 5x LNKxxx errors. Any tips?
Yes. How about pasting these so I/we don't have to guess what they might be referring to if you want us to say what is wrong?
wrote on 30 Jul 2022, 10:30 last edited by JacobottoIn any case using QCoreApplication::quit() doesn't work, so I have no idea when it's usable.
Commentinga->exec()
helps in this case, true, but this code is only part of the bigger program so I will see later how to handle.
Anyway I believe it's better to go fully Qt way, so using QObjects.
There are LNK errors, sorry it's in Polish partially, tried to change it, but only QtCreator interface changed. These errors means "not recognized external symbol":main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: virtual struct QMetaObject const * __cdecl Foo::metaObject(void)const " (?metaObject@Foo@@UEBAPEBUQMetaObject@@XZ) main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: virtual void * __cdecl Foo::qt_metacast(char const *)" (?qt_metacast@Foo@@UEAAPEAXPEBD@Z) main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: virtual int __cdecl Foo::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@Foo@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z) main.obj:-1: error: LNK2019: nierozpoznany symbol zewn©trzny "public: void __cdecl Foo::finished(void)" (?finished@Foo@@QEAAXXZ) przywoany w funkcji main main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: static struct QMetaObject const Foo::staticMetaObject" (?staticMetaObject@Foo@@2UQMetaObject@@B) debug\nienazwany1.exe:-1: error: LNK1120: liczba nierozpoznanych element˘w zewn©trznych: 5
-
In any case using QCoreApplication::quit() doesn't work, so I have no idea when it's usable.
Commentinga->exec()
helps in this case, true, but this code is only part of the bigger program so I will see later how to handle.
Anyway I believe it's better to go fully Qt way, so using QObjects.
There are LNK errors, sorry it's in Polish partially, tried to change it, but only QtCreator interface changed. These errors means "not recognized external symbol":main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: virtual struct QMetaObject const * __cdecl Foo::metaObject(void)const " (?metaObject@Foo@@UEBAPEBUQMetaObject@@XZ) main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: virtual void * __cdecl Foo::qt_metacast(char const *)" (?qt_metacast@Foo@@UEAAPEAXPEBD@Z) main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: virtual int __cdecl Foo::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@Foo@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z) main.obj:-1: error: LNK2019: nierozpoznany symbol zewn©trzny "public: void __cdecl Foo::finished(void)" (?finished@Foo@@QEAAXXZ) przywoany w funkcji main main.obj:-1: error: LNK2001: nierozpoznany symbol zewn©trzny "public: static struct QMetaObject const Foo::staticMetaObject" (?staticMetaObject@Foo@@2UQMetaObject@@B) debug\nienazwany1.exe:-1: error: LNK1120: liczba nierozpoznanych element˘w zewn©trznych: 5
wrote on 30 Jul 2022, 11:02 last edited by@Jacobotto said in Using QNetwork and QEventLoop, not working signals.:
In any case using QCoreApplication::quit() doesn't work, so I have no idea when it's usable.
It does work, you don't have your code right yet.
After you added
class Foo
withQ_OBJECT
, did you re-runqmake
? Given that you have linker errors I do not know what you are actually compiling/running with. -
wrote on 30 Jul 2022, 11:15 last edited by Jacobotto 8 Sept 2022, 19:58
Yes, I did clean, run qmake again and rebuild project. I'm using QtCreator on Windows 10, Qt6.2.3, MSVC2019 compiler, x64.
//EDIT - From what I read it can be an issue with .moc file, which is required when using QOBJECT macro with signals/slots and so on, I see it's not created, there are only these files in the build dir:
.. and inside "debug":
//EDIT2 - Solution: It is necessary to include .moc file (but I don't know how to generate it etc.) OR just declare QObject in the header file what I obviously did not <facepalm>, and every other declarations too! Lesson for every beginner, use .cpp and .h files, do not put everything in just .cpp file/s. Now moc files are generated automatically and the program works!
Second thing, no need to use eventloops but if somebody wants to (and how to quit them..): https://stackoverflow.com/questions/4180394/how-do-i-create-a-simple-qt-console-application-in-c
Regarding to my first post, I found it can be done better without unnatural eventloop:
"(...)get function is asynchronous, so at the point you are trying to read the answer the http request might not even be completed yet.
To solve this, you need to handle the finished signal:"connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
"and read the results in the handler:"
void NetworkHandler::replyFinished(QNetworkReply *reply) { qDebug() << reply->readAll(); }
Source: https://stackoverflow.com/questions/20441756/qt-how-can-i-send-https-request-to-server
//EDIT3
One more thing, as comment from above source says:
"you need an event loop to use QNetworkAccessManager. If your code does not run inside a QCoreApplication::exec() event loop then you're going to have to use the QEventLoop class, but be warned that it is considered to be sloppy code. –"So I want to present my final code for this if anybody is interested:
header file:#ifndef HEADER_NAME_H #define HEADER_NAME_H #pragma once #include <QCoreApplication> #include <QDebug> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QUrl> #include <QUrlQuery> #endif // HEADER_NAME_H using json = nlohmann::json; class Foo : public QObject { Q_OBJECT public: Foo( QObject* parent = 0 ) : QObject( parent ) { sendRequest(); } private: QNetworkAccessManager mgr; QNetworkReply *reply; void sendRequest(); signals: void finished(); private slots: void replyFinished(QNetworkReply *reply); };
main cpp file:
#include <header.h> void Foo::replyFinished(QNetworkReply *reply) { qDebug() << reply->readAll(); emit finished(); }; void Foo::sendRequest() { QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); // the HTTP request QNetworkRequest req( QUrl( QString("http://ip.jsontest.com/") ) ); mgr.get(req); }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Foo foo; QObject::connect( &foo, &Foo::finished, &a, &QCoreApplication::quit ); return a.exec(); }
Now all works fine and app quits properly with code 0.
Another interesting source I was basing on, it's about downloading the file but it doesn't matter, it's about almost the same:
https://www.bogotobogo.com/Qt/Qt5_Downloading_Files_QNetworkAccessManager_QNetworkRequest.php
1/7