Keeping GUI responsive
Basically I want to keep my GUI responsive while, for example, processing data in a loop. So I guess I would have to do that processing in another thread.
If I want to wait for a thread to finish before continuing in my main thread I would have to use a QEventLoop, right?
Here is my code which creates a new thread and does what it should, but doesn't seem to exit the QEventLoop ("end" doesn't get printed out):
@ MyWorker w;
connect(&t, &QThread::started, &w, &MyWorker::doLotsOfStuff); connect(&t, &QThread::finished, &l, &QEventLoop::quit); qDebug() << "begin"; w.moveToThread(&t); t.start(); l.exec(); qDebug() << "end";@
What am I doing wrong?
Help is appreciated! :)
Chris Kawa Moderators last edited by
t.start() calls QThread::run() in another thread, which in default implementation just calls exec(). What that means is that your thread never actually finishes and keeps going in circles after finishing doLotsOfStuff. You would need to call something like QThread::currentThread().quit() at the end of doLotsOfStuff or do something like
connect(&w, &MyWorker::someFinishedSignal, &t, &QThread::quit());
Oh, that makes things more clear. Thank you very much!
Take a look at the "example":/doc/qt-5.0/qtcore/qthread.html#details in the documentation.
You don't need the event loop in here. Instead, create your thread and your worker on the heap (using the new keyword) and listen for a signal from your worker when it is done.
Alright, I skipped the QEventLoop.
What if I want to cancel what the thread is processing (i.e exit a loop)?
I've added a signal to MainWindow called "abort()", added a slot to MyWorker called "abort()" and connected the both with:
@connect(this, &MainWindow::abort, w, &MyWorker::abort);@
And then when I click a button, I emit the signal with:
But whatever is in the "abort()" slot of MyWorker doesn't get executed.
Is this the wrong way of doing it?
No, that is ok in principle. However, it only works if your thread actually returns to the event loop.
[quote author="Andre" date="1360167554"]No, that is ok in principle. However, it only works if your thread actually returns to the event loop.[/quote]
Would you like to elaborate on this one? :)
For slots to work in a thread, the thread needs a running event loop. That means: exec() must have been called on it (done by default, so a vanilla QThread has one) and the thread must not be stuck in a busy loop.
If your thread is doing some huge calculation in one big chunk, and it thus never returns to the event loop, the event that wraps the signal will never be processed, and thus the slot won't be called. It is the same in the main event loop: if you do something very big, or do a busy wait or something like that, events won't be processed anymore, and your application will appear unresponsive to the user.
So, if you want to use a signal/slot for an abort, you will need to have a thread that stays responsive and regulary returns to the event loop. Note that you could probably also call processEvents() from within your loop or other time consuming procedure to reach the same effect. Not the nicest solution though.
As an alternative, considder creating a normal abort() method, that just sets a flag that you check from your time consuming procedures. Best use a QAtomicInt for such a flag.
Wow, thanks Andre!
So basically I should just add a method to MyWorker (and call it with worker->abort()) and that would work just fine?
Just out of curiosity (and because my knowledge is pretty inadequate), why use a QAtomicInt instead of say, a boolean or normal integer as a "flag"?
Sorry for my stupidity. :)
The check for the bool flag would be optimized out, as there is no code to change it inside the loop where it is checked over and over again. That doesn't happen with a QAtomicInt.