I tried to emit signal finished() from my class. But when I'm connecting the signal to my slot, it didn't do anything.
My class' name is blend_install, I declared it as blendinstaller and tried to connect that to the QEventLoop.
....
QEventLoop ac;
connect(&blendinstaller, SIGNAL(finished()), &ac, SLOT(quit()));
blendinstaller.show_progress();
blendinstaller.download(); // this will execute everything and in the end emit finished()
ac.exec();
....
The download() function:
current_prog = BLEND_INSTALL_NONE;
emit progress_changed(current_prog);
manager = new QNetworkAccessManager;
file_handler = new QFile(downloadTo);
file_handler->open(QFile::WriteOnly);
.... handle error .... // each of this (error handling) will emit finished() signal and return;
.... // each of this will represent the process of reporting event changes (for logging), emit a SIGNAL()
QNetworkRequest request;
request.setUrl(QUrl(downloadFrom));
reply = manager->get(request);
event = new QEventLoop;
connect(reply,SIGNAL(finished()),event,SLOT(quit()));
connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(downloadError(QNetworkReply::NetworkError)));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(downloadProgressL(qint64,qint64)));
event->exec();
.... handle error ....
.... write reply.readAll() to file ....
....
// these are instruction for a custom QProcess instance
proc.setProgram(extractWith);
proc.setArguments(ar);
proc.setWorkingDirectory(downloadIn);
event = new QEventLoop;
connect(&proc,SIGNAL(finished(int)),event,SLOT(quit()));
connect(&proc,SIGNAL(error(QProcess::ProcessError)),this,SLOT(extractError(QProcess::ProcessError)));
connect(&proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(extractFinished(int,QProcess::ExitStatus)));
proc.start();
proc.open_console();
event->exec();
.... handle error ....
....
.... attempt to find output of QProcess (extract an archive) ....
.... handle error, output of QProcess not found ....
....
emit installed(installOn);
emit finished(); // the SIGNAL I want to get.
qDebug("It's finished installing!");
So, TL;DR each of errors handling will return from the function but also emit finished() and in the end of function (assuming there is no error) it will emit finished().
It won't quit the loop.
Any idea?
The problem with your download() method is, that it is already a synchron method. You don't need this event loop. You already do everything in event loops inside your download() method.
Side note: And you seem to have some memory leaks, since you create QEventLoop without parent and never delete it.
UPDATE #1:
Your finished() event is not being handled by the outer QEventLoop (ac), because the finished() signal gets emitted before the QEventLoop even begins to handle events with exec().
As a ugly workarround you could invoke download() after exec() with a queued QMetaObject::invokeMethod() (Qt::QueuedConnection) call (but i would not recommend it).
Update #2
Here is a small example, which is not perfect, of course :P
class BlendDownloader
{
Q_OBJECT
public:
BlenDownloader() :
_file(NULL)
{
}
void download()
{
_file = new QFile("C:/myfile.blubb");
QNetworkRequest req("your url here");
QNetworkReply* reply = _mgr.get(req);
QObject::connect(reply, SIGNAL(finished()), this, SLOT(onDownloadFinished()));
// TODO: Also handle error callback here
}
private slots:
void onDownloadFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
reply->deleteLater();
// Write response data to file.
// Note: You might get problems with big files,
// since this buffers the entire response of QNetworkReply
// in internal buffer. It's better to use the "readyRead()"
// signal and write incrementally.
_file->write(reply->readAll());
// Do your parsing stuff now and emit "finished()" at the end.
// ... parsing, validation here ...
// Clean up
_file->close();
delete _file;
_file = NULL;
emit finished();
}
private:
QNetworkManager _mgr;
QFile* _file;
};
Related
My application makes use of QNetworkReply's for send and receiving data from a RESTful API.
There are many tutorials available for using the QNetworkReply with QNetworkAccessManager
Once such example which I used can be found here or even here
Basic Usage:
// Header
QNetworkAccessManager *manager;
QNetworkReply *myReply;
QMetaObject::Connection conReply;
// Making the request
void MainWindow::makeRequest(QString url) {
//...
QNetworkRequest request(QUrl(url));
myReply = manager->get(request) // or post(request)
conReply = QObject::connect(myReply, SIGNAL(finished()), this, SLOT(myReplyResponse()));
}
// Handling the request
void MainWindow::myReplyResponse(){
QObject::disconnect(conReply);
QByteArray data = myReply->readAll();
// or QByteArray data = myReply->read(myReply->bytesAvailable());
myReply->deleteLater();
// do something with this data
//...
}
Using a similar implementation, I request data every X seconds.
Problem:
When receiving the finished() signal, the code handling the reply is triggered, but when reading the data, I get a SIGSEGV.
This issue seems to occur at random, thus I cannot determine what triggers it.
Any suggestions would be gladly accepted.
What is probably happening is that it is delaying the order, let's say that an order is sent every second but it takes 2 seconds to replicate, after 2 seconds you have read the reply and you have deleted it from memory, when the other comes myReply is an empty pointer. What you must do is use sender() to obtain the replica, and it is always advisable to validate that you do not have an empty pointer:
*.h
private:
QNetworkAccessManager *manager;
*.cpp
[...]
manager = new QNetworkAccessManager(this);
[...]
void MainWindow::makeRequest(const QString &url)
{
Qurl mUrl(url);
QNetworkRequest request(mUrl);
QNetworkReply *myReply = manager->get(request); // or post(request)
connect(myReply, &QNetworkReply::finished, this, &MainWindow::myReplyResponse);
}
void MainWindow::myReplyResponse()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if(reply){
QByteArray data = reply->readAll();
qDebug()<<data;
reply->deleteLater();
}
}
I have the following two simple methods in a Qt Console Application:
void Webservice::getFile(PostElement el)
{
parameter = ⪙
QUrl url(PATH);
QUrlQuery query;
query.addQueryItem(el.getParam(), el.getValue());
url.setQuery(query);
request.setUrl(url);
manager->get(request);
connect(manager, SIGNAL(finished(QNetworkReply*)), this,
SLOT(downloadCompleted(QNetworkReply*)));
}
void Webservice::downloadCompleted(QNetworkReply *reply)
{
QByteArray b = reply->readAll();
qDebug() << "Download completed!";
QFile file("/tmp/" + parameter->getValue());
file.open(QIODevice::WriteOnly);
file.write(b);
file.close();
reply->deleteLater();
}
I created a QTest project to test getFile method.
#include <QtTest>
#include <QCoreApplication>
// add necessary includes here
#include "webservice.h"
class WebserviceTest : public QObject
{
Q_OBJECT
public:
WebserviceTest();
~WebserviceTest();
private:
Webservice ws;
private slots:
void initTestCase();
void cleanupTestCase();
void getFile();
};
WebserviceTest::WebserviceTest()
{
}
WebserviceTest::~WebserviceTest()
{
}
void WebserviceTest::initTestCase()
{
}
void WebserviceTest::cleanupTestCase()
{
}
void WebserviceTest::getFile()
{
PostElement el{"file", "myPic.png"};
ws.getFile(el); // it should wait for the slot to be executed
}
QTEST_MAIN(WebserviceTest)
#include "tst_webservicetest.moc"
As you can see, it has signals and slots. If I run that one on my original project, I see that downloadCompleted executes without issues (because it is using the Event Loop). But in the test project the slot is not called. I googled and found some examples using QSignalSpy, but at the moment I have not executed the slot successfully.
How to tell my test to "wait" for the slot to be completed?
That's exactly the purpose of QSignalSpy, it basically spins new QEventLoop that waits for signal to be executed. Your tests starts and asynchronous operation but it doesn't wait for it to finish so it will destruct objects while the operation is ongoing.
Qt test also has some built-in timeouts that prevents tests to be stuck which are stopped when QSignalSpy is used. In your case you need to emit new signal from your class Webservice indicating that download is finished, e.g. (you can also pass arguments to the signal) if needed:
class Webservice : public QObject {
Q_OBJECT
// Constructors etc.
signals:
void finished();
}
Then at the end of your void Webservice::downloadCompleted(QNetworkReply *reply):
emit finished();
and in your test something like this:
PostElement el{"file", "myPic.png"};
// Prepare spy
QSignalSpy spy(&ws, SIGNAL(finished));
ws.getFile(el);
// Wait for result - by default this waits 5 seconds
spy.wait();
// Check if we had a timeout or actually signal was raised
QCOMPARE(spy.count(), 1);
I have done an application with some threads. Everything seems to work ok if I call my stopConsumer inside a keypressedEvent. But If I call it inside a destructor of closeEvent.. it fails.
My QThread class that has a run method like this one:
void Consumer::run()
{
forever {
// do something something
// do something something
// do something something
//-------------------------------- check for abort
abortMutex.lock();
if(abort) {
abortMutex.unlock();
qDebug() << "abort..";
break;
} abortMutex.unlock();
//-------------------------------- check for abort
}
qDebug() << "Consumer > emit finished()";
emit finished();
}
void Consumer::stopConsume() {
abortMutex.lock();
abort = true;
abortMutex.unlock();
}
and a method in the MainWindow:
void initConsumers()
{
consumer1 = new Consumer(....);
connect(consumer1, SIGNAL(finished()),
this, SLOT(deleteConsumer()));
consumer1->start();
}
void stopConsumer() {
if(consumer1!=NULL) {
qDebug() << "stopConsumer";
consumer1->stopConsume();
}
}
If I have a keypressed that calls stopConsumer.. it's ok, deleteConsumer is reached.
If I call stopConsumer inside the MainWindow destructor or inside a MainWindow closeEvent.. the slot deleteConsumer is never reached!
Any ideas?
Given that the Consumer class and your MainWindow have different thread affinities, the call you make to connect inside initConsumers() is likely using a Qt::QueuedConnection, which means that the deleteConsumer() slot won't get called immediately.
If you would like to ensure that the consumer gets deleted from the destructor of your main window (or equivalently, from a close event), one possible solution is to call stopConsume() on the consumer, then wait until the thread is no longer running (see http://qt-project.org/doc/qt-5.1/qtcore/qthread.html#isRunning), then call deleteConsumer() directly.
Update
Here's an example of what I described above:
consumer1->stopConsume();
consumer1->wait();
deleteConsumer();
It's not advisable to switch the connection type to Qt:DirectConnection since that will cause the deleteConsumer() function to be called from the body of Consumer::run(), which will likely crash your application.
Part of the problem here is that you're deriving from QThread, which is not how it is supposed to be used. You can read about why deriving from QThread is wrong here.
Instead, what you should be doing is deriving your class from QObject, creating a QThread object and moving the derived QObject instance to that thread.
class Consumer : public QObject
{
...
signals:
void finished();
private slots:
void run();
}
QThread pThread = new QThread;
Consumer pObject = new Consumer;
// move the pObject to the thread
pObject->moveToThread(pThread);
You can then control the thread with signals and slots.
// assuming you've added a run slot function to the Consumer class
connect(pThread, SIGNAL(started()), pObject, SLOT(run()));
connect(pObject, SIGNAL(finished()), pThread, SLOT(quit()));
connect(pObject, SIGNAL(finished()), pObject, SLOT(deleteLater()));
// Note the thread cleans itself up here, but if the app is quitting,
// waiting on the thread to finish may be required instead
connect(pThread, SIGNAL(finished()), pThread, SLOT(deleteLater()));
And start the thread: -
pThread->start();
Used this way, it also enables multiple objects to be moved to a single new thread, rather than creating a new thread per object instance.
I have a simple application that uses one worker thread.
This worker thread is started and initializes DownloadManager, which is responsible for downloading files from the net.
In my main application class I have the finished() SIGNAL on the thread that is emitted before the DownloadManager finishes.
My question is how to make the worker thread wait until the DownloadManager finishes its work.
Here is example code :
class Main
{
m_DownloadWorker = new DownloadWorker(this);
QObject::connect(pm_hotosDownloadWorker, SIGNAL(finished()), this, SLOT(DownloadWorkerFinished()));
m_DownloadWorker->Execute();
// do i need to do here something so the thread will wait ?
.....
void Main::DownloadWorkerFinished()
{
Log("DownloadWorkerFinished");
}
};
class DownloadWorker : public QThread
{
void DownloadWorker::Execute()
{
// do i need to do here somthing so the thread will wait ?
start();
}
void DownloadWorker::run()
{
// do i need to do here somthing so the thread will wait ?
DownloadManager* pDownloadManager = new DownloadManager(this);
pDownloadManager->download();
}
};
class DownloadManager: public QObject
{
// downloading stuff using Qt networkmanager
.....
.....
}
In cases when you have a signal that is emitted when an asynchronous operation is completed, you can always use QEventLoop to "turn" the asynchronous operation into synchronous with the current thread. It is still asynchronous, but the thread will "wait" for it to finish.
QNetworkAccessManager nam;
QEventLoop loop;
QNetworkReply *reply = nam.get(request);
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
So basically you put this in you DownloadManager class where you want to do a network request synchronously. exec() will return once the loop's quit slot has been called.
You can use QThread::exec() call to run your thread in the event loop. The thread will run it until you tell your thread to exit by calling QThread::exit(). So some sample code can look like this:
void DownloadWorker::run()
{
DownloadManager* pDownloadManager = new DownloadManager(this);
connect(pDownloadManager, SIGNAL(finished()), SLOT(exit()));
connect(pDownloadManager, SIGNAL(error()), SLOT(exit()));
pDownloadManager->download();
exec();
}
That would guarantee you that your thread won't quit until the "finished()" signal of your DownloadManager is issued.
Note: Here I put an example of how to solve your problem but I don't know your whole app code. That means there is not guarantee this code is thread safe and consistent. You need to take care of the mutexes and all the correct synchronization yourself. Be very careful ! Working with such a "low level" thread API requites good understanding of multithereading.
Hope that helps
I use QNetworkAccessManager to do form POST.
I have connected signals and slots as:
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(readCookies(QNetworkReply*)));
Now, I make a request by doing:
manager->post(request,postData);
Now readCookies(QNetworkReply *) will be run as soon as SIGNAL is emitted. Now, using the Cookies which I get in this slot, I have to make one more POST..
As signals & slots are asynchronous, I want to wait till I get the cookies from my first POST and then I again want to do another post using the cookies I got in first POST like
//Setting new request, headers etc...
manager->post(request2,postData2);
I want the later to always be executed after first one has executed (so that I get proper cookies value).
What is the way to go? I am new to all these SIGNALS & SLOTS so please bear with me.
You can do the post in your readCookies() slot:
void readCookies( QNetworkReply* reply ) {
if ( ...error? ) {
report error...
return;
}
...
manager->post(request2,postData2);
}
I will be called when the cookies is read, and you can then continue with your post. Connect that to a second slot, and so on.
Managing multiple, possibly parallely running asynchronous operations like this can become errorprone though, if you manage many of them in a single object. I would suggest to use the Command Pattern - here I described why I find it extremely useful in exactly this context. The sequence of request and asnychronous operations is encapsulated in a single object (abbreviated, with some pseudo-code):
class PostStuffOperation : public QObject {
Q_OBJECT
public:
enum Error {
NoError=0,
Error=1,
...
};
Error error() const; //operation successful or not?
QString errorString() const; //human-readable error description
... setters for all the information the operation needs
...
void start() {
...start your first request and connect it to cookiesRead
}
public Q_SLOTS:
void cookiesRead( QNetworkReply * ) {
if ( error ) {
// set error and errorString...
emit finished( this ); //couldn't read cookies, so the operation fails
return;
}
... do post
}
void postFinished( QNetworkReply* ) {
if ( error ) {
// set error and errorString...
}
emit finished( this ); //post finished - that means the whole operation finished
}
Q_SIGNALS:
void finished( PostStuffOperation* );
};
To start the operation, you do
PostStuffOperation op* = new PostStuffOperation( this );
... pass data like server, port etc. to the operation
connect( op, SIGNAL(finished()), this, SLOT(postOperationFinished()) );
op->start();
void postOperationFinished( PostStuffOperation* op ) {
if ( op->error != PostStuffOperation::NoError ) {
//handle error, e.g. show message box
}
}
It makes sense to have a common baseclass for such operations, see e.g. KDE's KJob.
You can connect a signal from this to a slot from your manager and emit the signal after reading the cookies. By example:
connect(this, SIGNAL(cookiesRead()), manager, SLOT(PostAgain());
So your readCookies function will be:
{
// Read cookies
emit cookiesRead();
}
Of course you can send all data you want form signal to slot.
Hope that helps
You can send a second signal connected to another slot (the resend slot), if you have finished the evaluation of your first cookie. You can do that directly in the slot. You can also call slots like a normal member function.