[Solved] GUI unresponsive during intensive computation
-
I don't see how your MainWindow.h code would compile - are you trying to initialize the thread and the updater members there?
If I understand correctly, you have a worker thread, and you want to notify the main UI thread that it finished work with a certain result? You can emit a signal from that worker thread, and connect that signal to a slot in your main window.
-
If you have something happening in a worker thread, can't you pass it all the information so that it does all the work in that thread?
If getHash() is public, and you pass a pointer to your worker thread so that it can access the MainWindow object, then you can call getHash() and it will be executed in the worker thread. But I think this is really complicating things.
-
If you have a long procedure, perhaps executing a for loop, sprinkle some of these in your code
qApp->processEvents(QEventLoop::ExcludeSocketNotifiers,10);
This will give the app a few (10 in this case) milliseconds to handle window events
A better way would be do put rethink your worker thread so it does not block
-
Okay, so I copied everything from the MainWindow::getHash() function over to a member function in the worker object. Then I connected a signal/slot and it works.
However, I would rather just have the function be a member function of MainWindow.
[quote author="frankiefrank" date="1401064145"]If you have something happening in a worker thread, can't you pass it all the information so that it does all the work in that thread?
If getHash() is public, and you pass a pointer to your worker thread so that it can access the MainWindow object, then you can call getHash() and it will be executed in the worker thread. But I think this is really complicating things.[/quote]
How would I pass a pointer to the worker thread?
-
From the code parts you posted I understand you have your Updater object which you move to the worker thread. You can store a pointer to MainWindow as a member of Updater, and set it via the constructor.
Of course, if you access the same code both in the Main UI thread and in the worker thread - without any thread safety mechanics - you may end up in trouble.
-
-
Maybe there are other ways, I can't think of them right now and even these ones may be a bit too complicated for the problem you presented.
-
-
I'd recommend separating your business logic/algorithm methods from your GUI classes. This would allow you to follow the object-oriented single responsibility principle better.
If you moved your getHash function to a class of your own that inherits from QObject, you will be able to do something like this:
@MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->textBrowser->setFont(QFont("Monospace",11));
ui->textBrowser->setLineWrapMode(QTextEdit::NoWrap);// Make sure not to parent updater, so you can move it to a thread GUIUpdater* updater = new GUIUpdater(); QThread* pthread = new QThread(this); updater->moveToThread(pthread); connect(this,SIGNAL(getHash()),updater,SLOT(getHash())); connect(updater,SIGNAL(req()), this, SLOT(getCheckSum())); connect(updater,SIGNAL(Finished()),pthread,SLOT(quit())); connect(pthread, SIGNAL(finished()), updater, SLOT(deleteLater()));
}@
To call a slot in MainWindow from an updater method, you can use signal and slots like the req() signal you connected in the MainWindow constructor. This will be an asynchronous method call; when you emit the signal from updater, execution will continue in the updater method. The main/GUI thread will have the slot waiting in its event queue.
If you need a direct function call, in the case where the timing is important or the doWork method needs a return value from a class on the main/GUI thread (in your case, MainWindow), I would recommend refactoring your doWork method. Make the place where you emit the signal to cause the main/GUI thread to compute be the last line of your doWork method. Then, in the class on the main/GUI thread (MainWindow), your slot will do your computation, and then emit a new signal that will be connected to a new slot containing the rest of the code that was previously in your doWork method after the signal emission.
It will look like this:
@MainWindow::MainWindow()
{
connect(worker, SIGNAL(doGUISlot()), this, SLOT(doImportantComputation()));
connect(this, SIGNAL(doRestOfWorkSlot()), worker, SLOT(finishWork()));
}Class::doWork()
{
// Do the first part of your work here, then emit to allow GUI to compute. GUI will signal when it's done to
// cause finishWork to be executedemit doGUISlot();
}
MainWindow::doImportantComputation
{
// Do computation here, then go back to Worker::finishWorkemit doRestOfWorkSlot();
}
Class::finishWork()
{
// Finish work
}@