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!)...
Related
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.
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.)
I'm using a library where I need to call a trigger function that starts some processing (starts a thread that does the job) and returns immediatly. Then when the processing is finished a signal is emitted.
This needs to be done periodically based on different parameters. Since other calls to the trigger function must not be done while processing I would need to queue them in some way. I thought about using a QEventLoop ("loop") but without luck so far.
Please look at this piece of code:
test::test()
{
connect(&timer, SIGNAL(timeout()), this, SLOT(timerSlot()));
connect(&timer2, SIGNAL(timeout()), this, SLOT(timer2Slot()));
connect(&library, SIGNAL(processingFinished()), &loop, SLOT(quit()));
timer.setInterval(2000);
timer.start();
timer2.setInterval(4000);
timer2.start();
}
void test::timerSlot()
{
loop.exec();
startProcessing(some_parameters);
}
void test::timer2Slot()
{
loop.exec();
startProcessing(some_other_parameters);
}
The issue is that when loop.exec() is called while processing I get the message:
QEventLoop::exec: instance xxxxxx has already called exec()
What would be the right way to do what I intend?
Thanks in advance.
One simple solution is to introduce a member variable, for example, bool m_isProcessing, start processing only if m_isProcess == false, then set it to true when you start processing and reset it to false when processing is complete. Since the slots for your test QObject execute on the GUI/main thread, you don't need to worry about synchronization between timer slots and the slot that will execute when processing is finished.
If you want to keep track of events that occur during processing, you can use the same method: introduce a member variable to the test class to track the information you need.
It seems that you are looking for Qt::QueuedConnection.
Qt::QueuedConnection 2 The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
Therefore, you could write something like this:
connect(&timer, SIGNAL(timeout()), SLOT(timerSlot()), Qt::QueuedConnection);
connect(&timer2, SIGNAL(timeout()), SLOT(timer2Slot()), Qt::QueuedConnection);
For details, you could look into the well-known mandelbrot example how this is done in there, although it is using worker threads:
Mandelbrot Example
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
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.