Qt: Properly use QNetworkReply? - c++

How to properly use the QNetworkReply? I have seen example like this:
void HttpDownload::on_downloadButton_clicked()
{
// get url
url = (ui->urlEdit->text());
// get() method posts a request
// to obtain the contents of the target request
// and returns a new QNetworkReply object
// opened for reading which emits
// the readyRead() signal whenever new data arrives.
reply = manager->get(QNetworkRequest(url));
// Whenever more data is received from the network,
// this readyRead() signal is emitted
connect(reply, SIGNAL(finished()),
this, SLOT(handleFinish()));
}
Is it possible that the finished signal is emitted before the connection is build?

Check also errors
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkErrorSlot(QNetworkReply::NetworkError)));
I think that finished signal is emitted also in the error situations and it may look like finished is emitted before the connection is built.

Certainly. The finished signal is emitted as soon as the connection cannot proceed anymore and will not be emitting any more signals. The signal's meaning is, essentially "I'm done emitting signals and my state is now set". Any unrecoverable error condition will, by necessity, end in the emission of the finished signal.

Related

Asynchronous function calls in Qt

I'm pretty new to Qt and programming and are facing a problem I can't find a solution for.
I want to read some information from an online XML file and send it to my main program.
To do so, I created a class XMLParser and added the following to the constructor:
XMLParser::XMLParser(QString searchstring)
{
QNetworkAccessManager *manager2 = new QNetworkAccessManager(this);
reply = manager2->get(QNetworkRequest(QUrl("http://www.boardgamegeek.com/xmlapi/search?search="+searchstring)));
XMLParser::connect(reply, SIGNAL(finished()),
this, SLOT(fileIsReady()) );
}
and fileIsReady fills a QMap and stores it as a private class member.
In my second class, I call
XMLParser *xmlpars = new XMLParser(input_gamename->text());
QMap<QString, int> searchResults = xmlpars->getSearchList();
and getSearchList is a simple getter function.
The problem is, that getSearchList is executed before fileIsReady finished reading the XML file and returns an empty map.
From what I understand, the constructor should not be finished until fileIsReady() finished its work. And thus, getSearchList() shouldn't be called early.
My two questions:
Why does my programm progresses while the function didn't finish reading.
How can I make the second call "getSearchList" wait?
Thanks a lot in advance!
First, you need to understand the fundamental concept of signals and slots.
After you make a connection, the slot will get called every time the signal is emitted.
The connect() functions returns after connecting the signal to the slot. It doesn't wait for the signal to be emitted.
In your XMLParser constructor, your connect() function registers this: "When the finished() signal is emitted, run the fileIsReady() function".
Now, to answer your questions.
Why does my programm progresses while the function didn't finish reading.
Because in your constructor code, you asked the constructor to finish after you connect the signal to the slot. You did not ask it to wait for the download to finish.
And then, you call getSearchList() without waiting for the finished() signal. So, getSearchList() gets called before fileIsReady().
How can I make the second call "getSearchList" wait?
Like MrEricSir said, you shouldn't ask it to wait! (Think about it: What happens if you lose internet connection and can't finish downloading the file? The answer is, your program will freeze because it will wait forever. That's bad.)
Don't call getSearchList() immediately after constructing XMLParser. Instead, make XMLParser emit a "finishedParsing()" signal when it finishes parsing the XML file. Then, make another signal-slot connection: Connect the finishedParsing() signal to a slot that calls getSearchList().
So, I found a solution using QEventLoop. But as far as I read, this isn't recommendend.
Are there any other solutions? And why is using QEventLoop bad habit (That's what I read from other answers here StackOverflow).
XMLParser::XMLParser(QString searchstring)
{
QNetworkAccessManager *manager2 = new QNetworkAccessManager(this);
reply = manager2->get(QNetworkRequest(QUrl("http://www.boardgamegeek.com/xmlapi/search?search="+searchstring)));
QEventLoop loop;
XMLParser::connect(reply, SIGNAL(finished()),
this, SLOT(fileIsReady()) );
XMLParser::connect(this, SIGNAL(finishedReading()),
&loop, SLOT(quit()));
loop.exec();
}
How can I make the second call getSearchList wait?
You don't! Instead, just move any code that expect the XML file to be downloaded into the fileIsReady() slot that you've already defined. That way your program won't lock up while it's waiting for the download to complete (which is the entire point of asynchronous programming.)

pthread 2 signals and slots wrapper mit QEventLoop

problem
i'm currently putting FUSE together with qt5. there is no bridge between Qt and FUSE yet, both the FUSE main thread (which is spawning the other working FUSE threads) and the QCoreApplication are simply running side by side.
but i want to be able to send and receive data between a QObject based object and the pthread's Read(..) function shown in [0] using Qt's SIGNALS and SLOTS.
question
now i want to alter the Read(..) function from [0] to retrieve data using Qt's SIGNALS and SLOTS from a QObject based class. sending a signal from a pthread works but without an explicit QEventLoop i can't receive the reply. therefore i was looking at the code from [1] which is excellent in design but i didn't get it working yet.
pseudo code (taken from [1]):
QNetworkAccessManager qnam;
QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
/* reply has finished, use it */
looks interesting, all i would need is a QObject deriving class similar to the QNetworkReply which would handle the request.
when i was playing with that code i had the problem that my implementation of QNetworkReply wouldn't wait for loop.exec() to be running and then the finished() SIGNAL wouldn't be received by the loop.
but isn't there something easier than to spawn a QEventLoop?
NOTE: the QNetworkReply and QNetworkAccessManager in the example from [1] is spawned inside the pthread, i however, need to be able to communicate with the QCoreApplication's even queue using SIGNALS and SLOTS since the object with the data in it comes from a different QThread (in this either the QCoreApplication or a special QThread).
using a Qt::QueuedConnection
i've also found [2] and maybe:
connect(src, SIGNAL(signal-signature), dest, SLOT(slot-signature), Qt::QueuedConnection);
is all i want but i doubt that.
links
[0] https://github.com/qknight/qt-fuse-example/blob/4d92a74fad22fd559588e58be67f766174c7efb8/qt-fuse/examplefs.cc#L74
[1] http://qt-project.org/wiki/ThreadsEventsQObjects#7494f2b4836907fc1c09311e3a0305e6
[2] emit Qt signal from non Qt Thread or ouside Qt main event loop with at 4.5
What you're likely facing is that QNetworkAccessManager internally uses threads to process http requests. That's why it "doesn't wait" for you. There's a rather simple modification needed to fix it:
QNetworkAccessManager qnam;
QEventLoop loop;
QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
if (!reply->isFinished()) loop.exec();
Things to note when using QObjects in Multiple Threads
When the object that is the source of a signal lives (is instantiated in) a thread different than the thread of the object with slots, the connection will be of the QueuedConnection type automatically.
The real issue is: Each QObject has a thread affinity. The default affinity is the thread where the object was instantiated. You're not supposed to use such objects directly from other threads.
What you're likely doing is instantiating the sender and receiver objects in the same thread, but then emitting the signal from another thread. This is a source of potential errors error and leads to undefined behavior if the user of such an object is forcing a non-automatic direct connection.
Whenever you do emit object->signal(...), the following invariant should hold:
Q_ASSERT(QThread::currentThread() == object->thread());
Feel free to add those invariant checks in front of every emit() that you explicitly perform.
If the assertion fails, you need to use QObject::moveToThread to move the object to the thread where you want to fire its signals. You can get a QThread for a given pthread by calling QThread::currentThread() from the code that runs in the pthread. An instance of a QThread will be created automagically for you :)
Yes, you want the Qt::QueuedConnection method. But also ensure that you are using the multithreading Qt library. IIRC it is a build-time option.
See also: Qt documentation

QTcpSocket's QThread not emitting finished signal

I have created a (somewhat) simple telnet server, which creates a new thread for each connection:
void TelnetServer::incomingConnection(qintptr socketDescriptor)
{
TelnetConnection *thread = new TelnetConnection(socketDescriptor, this);
connect(thread, SIGNAL(shutdownRequested()), m_controller, SLOT(shutdown()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
After I disconnect the telnet client, I expected the deleteLater() slot to be called. However, findchildren of the telnet server shows that my QThread object for the (just disconnected) session still exists. It's as if the finished signal were not emitting.
As an experiment I tied the finished signal to a function that Qdebug's "FINISHED"...and it never appears. Can someone explain why the finished signal is not emitting after I disconnect the telnet client?
I am assuming that calling thread.disconnectClient() terminates the thread...but perhaps that's an incorrect assumption? To I have to quit the exec loop for the thread?
If you disconnect a running thread, and you do not have proper treatment, the finished signal is not supposed to be called, so this is normal.
You could hook up the delete later or the disconnect "signal" that you establish, but you need to make sure to properly quit the thread.

Could this code potentially run into an infinite loop?

Is it possible for this Qt code to run into an infinite loop?
QNetworkAccessManager m;
QNetworkReply *reply = m.get(QNetworkRequest(QUrl("http://www.example.org/")));
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
qDebug() << reply->readAll();
delete reply;
This is basically a "synchronous" way to display the contents of a webpage.
I have not observed any issues using it, but I considered the following scenario:
The finished signal of reply is emitted before the event loop is created and the signal-slot connection between finished and quit is made
No signal will be emitted from that point forward thus never triggering quit
loop.exec() will continually loop
Is it possible for that to occur, or am I not understanding something about how the Qt event loop works?
While Qt executes your own code (the code above, for example), it can't process new signals and call slots. Every signal was emitted while your method is executing will be processed later. If you want to force processing of signals in middle of your method, you can call QCoreApplication::processEvents to process signals in your current event loop or QEventLoop::exec to do it in another loop.
So this code is safe. If a signal comes too fast, it will wait in the event queue.
If you're emitting a signal and there are slots connected to this signal using Qt::DirectConnection, these slots will be executed immediately. But this doesn't match your case. Qt have to execute some internal code before the singal will be emitted. It can't execute this code while your method is executing. There are only one thread and only one call stack.
Note that when you're using Qt threads event loops' behaviour is more complicated.
Just before running loop.exec(), you could check reply->isFinished(). If it is, just don't execute the loop.

Process Signals & Slots Using a Thread

I need to be able make a request GET request, which requires the use of signals to process the replies, and return the reply back in a a processed form. I've structured the code as described in Threading Wihtout the Headache, but when I run the reply is never received or processed.
// Set-up request
QNetworkAccessManager* pConnection(new QNetworkAccessManager());
connect(pConnection,
SIGNAL(finished(QNetworkReply*)),
this,
SLOT(process(QNetworkReply*)));
QUrl url;
url.setUrl(HOST);
url.addQueryItem("P1", "Hi");
url.addQueryItem("P2", "Bob");
// Send request
QNetworkRequest request(url);
pConnection->get(request);
// Wait for reply
QThread* pResponce(new QThread(this));
connect(this,
SIGNAL(processingFinished()),
pResponce,
SLOT(quit()));
pConnection->moveToThread(pResponce);
pResponce->start();
pResponce->wait();
pConnection->deleteLater();
pResponce->deleteLater();
return this->processedReply;
Obviously I'm missing something, but what? It works when I'm not using the threads (and the deletes), so it's not the URL or server.
Only the main/gui thread receives signals if you don't set up an event loop in the other threads.
If no event loop is running, events won't be delivered to the object. For example, if you create a QTimer object in a thread but never call exec(), the QTimer will never emit its timeout() signal. Calling deleteLater() won't work either. (These restrictions apply to the main thread as well.)
Source: http://doc.qt.digia.com/4.6/threads-qobject.html
See QThread::exec(). That's why in the example you're linking the QThreadEx class is introduced, and used (not in your code snipplet, though!)...