PDA

View Full Version : QThread and GUI



jesse_mark
11th January 2013, 18:42
Hello,

I am trying to use QThread with GUI application, so the GUI wont stall on me.

in th GUI, i have a window through witch i load a Txt file and parse it and do processing on it, they are in this manner


loadFile (... )
{
readFile(..);
parseFile(..);
prossValues(...);
}

when i run the loadFile slot, it takes long time, and the GUI stalls.

what i did so far, is

I sub-classed the QThread,
override the run method and,
emit a signal called loadFile(). in the run method.


In the class where i need to load the file ,
I create a mythread and,
connect the loadFile signal to a loadFile slot.

the the signal triggers fine and i load the file, but the gui still stalls,
which i think it means that im still running on the main thread. not on the thread i created.

so, Please let me know what im missing and what the mistake im making and how can i fix it.

Thanks

Jesse.

wysota
11th January 2013, 19:06
We'd need to see the actual code. Bear in mind that QThread object lives in the main thread and not in the thread it represents, that's probably your issue.

Anyway, I suggest you use QtConcurrent::run() instead of a standalone QThread. For such simple usecases it's much easier to use.


QString loadTextFile(const QString &fileName) {
QFile f(fileName);
if(!f.open(...)) return QString();
QTextStream stream(&f);
return stream.readAll();
}

// ...
QFutureWatcher<QString> *futureWatcher = new ...; // optional
connect(futureWatcher, SIGNAL(finished()), this, SLOT(...))); // optional
QFuture<QString> future = QtConcurrent::run(loadTextFile, QStringLiteral("somefile.txt"));
futureWatcher->setFuture(future); // optional

// ... do something in the meantime

QString content = future.result(); // will block if the result is not ready (alternative to useing QFutureWatcher)

jesse_mark
11th January 2013, 21:37
one of the reasons that im using Qthread, that i want to learn and understand how it works and how i can use it correctly.

Here is the QThread subclass and the other class and i how i am using it.



#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void run();


signals:
void loadClicked();

public slots:

};

#endif MyTHREAD_H



//MyThread.c++

#include "mythread.h"
#include "QtCore"

MyThread::MyThread(QObject *parent) :
QThread(parent)
{

}
void MyThread::run()
{
qDebug() << "started...";

emit loadClicked();

}



//// Myclass.c++
// Here it
MyClass::Myclass(....)::QWidget(parent),
ui(new Ui::MyClass)
{
...
...
loadThread = new Mythread(this);

connect (loadThread,SIGNAL(loadClicked),this, SLOT(loadFile));
connect (loadThread,SIGNAL(finished),this, SLOT(onFinised));
...
}

MyClass::onLoadFile_Clicked()
{
loadThread->start();
}

MyClass::onFinised()
{
...
....
Qdebug() << "Finised.." ;
}

void MyClass::loadFile ()
{
QStringList fileLines readFile(filename);
result ParseFile(fileLines); // in this method i update the value of a progressBar
prossResult (result); // in this method i update the value of a progressBar
}

}



Added after 11 minutes:

I think i understand what is my mistake now, Do i need to write all the loadFile code in the run method of the Thread ?, in other words all the work that i need to make it run by the thread should be written in the Mythread class ??? and then pass the result back where i want it ??

wysota
11th January 2013, 21:58
Well... what you code does is that it starts a thread, emits a signal from it and immediately ends the thread. Your slot is executed as usual in the main thread. What you are trying to do is really not a good usecase for a QThread. You can see that you have written almost 80 lines of code to do such a simple thing as loading a file (and it still doesn't work). The solution I gave you in 16 lines also uses a thread but is much more compact to write and easier to use. A good usecase for using QThread is when you want the thread to run for a longer time period, possibly waiting for more job to come. For a come-and-go situation, you're likely to waste more time starting the thread than gain from executing it. In turn Qt Concurrent keeps a number of threads at hand that may already be alive and just waiting for more work to appear in which situation the latency needed to start a thread is being amortized among tasks executed throughout the lifetime of the thread.

If you insist on using QThread then I suggest that you don't subclass it. Instead push an object that will take care of parsing files to the thread and let it wait there for work, e.g.:


class Parser : public QObject {
Q_OBJECT
public:
Parser() {}
public slots:
void parseFile(const QString &fileName) {
result r = doTheParsingHere(fileName);
emit ready(r);
}
signals:
void ready(result);
};

// ...

QThread t; // create a thread controller (possibly a class member variable)
t.start(); // start a thread
Parser *p = new Parser; // create the parser object
p->moveToThread(&t); // push it to the thread
connect(p, SIGNAL(ready(result)), this, SLOT(useResult(result))); // connect to the result

// ...

QMetaObject::invokeMethod(p, "parseFile", Qt::QueuedConnection, Q_ARG(QString, "someFile.txt")); // make the parser do some work
// or alternatively to the last line:
connect(this, SIGNAL(parseRequested(QString)), p, SLOT(parseFile(QString)));
emit parseRequested("someFile.txt");

That's still over 20 lines of code and the thread is running doing nothing most of the time so using QtConcurrent is still superior.

jesse_mark
11th January 2013, 23:02
Thank you so much for the clear explanation.

I will diffidently go with the QtConcurrent::run method.

Just to make things clear, the parsing the file may sound not taken too much time, but in this case I parse the file, i use the data parsed(process it) and then show the result. this all three steps takes a long time in my case as the lines i need to parse reach up to N=2 Millions lines and when i process them I need to go N*N as well.. which really take a long time.

one Question regarding QtConcurrent::run, when I gave the method to QtConcurrent::run to run the method... is the thread that is used to run in it can reach the objects that are owned by the main thread.
I mean such as the the editeLines, plot ...etc ?? as i need to reach them to read and update them during this process.

Can I use it, to run the method, where the method reads some input from the UI objects ?? and then post other reseluts on the UI ??
myMethod()
{
fileName = LineEdite->text();
.....
.....
.....

}


I tried to call it like : QFutur<void> future = QtConcurrent::run(myMethod()); but it complains, so how i use it in this case ??

Thanks

wysota
11th January 2013, 23:37
Just to make things clear, the parsing the file may sound not taken too much time, but in this case I parse the file, i use the data parsed(process it) and then show the result. this all three steps takes a long time in my case as the lines i need to parse reach up to N=2 Millions lines and when i process them I need to go N*N as well.. which really take a long time.
This might also be a task for QtConcurrent, QtConcurrent::mapped() or mappedReduced() this time.


one Question regarding QtConcurrent::run, when I gave the method to QtConcurrent::run to run the method... is the thread that is used to run in it can reach the objects that are owned by the main thread.
If you synchronize threads properly then yes, without proper synchronisation -- no.

I mean such as the the editeLines, plot ...etc ?? as i need to reach them to read and update them during this process.
No, you can't access GUI objects from threads other than the main thread, this will (eventually) lead to a crash.


I tried to call it like : QFutur<void> future = QtConcurrent::run(myMethod()); but it complains, so how i use it in this case ??
You have to pass a pointer to the function and not the result of the function call (as you do above). Take a look at the code I posted or read the docs (or both).

anda_skoa
14th January 2013, 10:29
I wouldn't use QtConcurrent::run() for loading.
QtConcurrent::run() is based on the global thread pool and especially an I/O bound operation will keep anyone else waiting.

I would rather go for my own threadpool instead.

Cheers,
_

P.S. I also had the problem once that QtConcurrent::run() would execute in the calling thread. Might have been a bug (only occured on some users' systems). In any case I am not trusting that one anymore if I really require it to be run in a separate thread