Keeping GUI responsive
-
Hi again!
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;
QThread t;
QEventLoop l;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! :)
-
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());
@ -
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:
@emit abort();@
But whatever is in the "abort()" slot of MyWorker doesn't get executed.
Is this the wrong way of doing it?
-
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. :)