PDA

View Full Version : how to execute a QsqlQuery on a different thread ?



toufic.dbouk
8th September 2013, 23:04
hello there ,
was wondering what is the best way to execute a QsqlQuery on a different thread so that the GUI doesn't freeze
should i make a class that inhirits Qthread and connect to the data base in the run method and do the query ?
can emit a signal from the GUI class containing the input of the query ( example number ) and catch that signal when emitted in the thread then
do the query ?
or should i try another way ? maybe conecting the method that is repsonsible for the query to a thread by the thread started signal ?

wysota
9th September 2013, 07:18
Clone the database connection, open it, run your query, close and remove the connection. Everything apart cloning has to be done from the thread that is to execute the query.

anda_skoa
9th September 2013, 07:28
If you want to use signal/slots to communicate from the GUI to the worker thread then you'll need a QThread that runs an event loop.

You start by creating a QObject subclass that has the receiver slot and that slot does the database query.

You create an instance of QThread and an instance of this new class and then use moveToThread() to make it run in the worker thread.

The slot then gets invoked in the context of the worker thread.

Cheers
_

toufic.dbouk
9th September 2013, 11:15
If you want to use signal/slots to communicate from the GUI to the worker thread then you'll need a QThread that runs an event loop.

You start by creating a QObject subclass that has the receiver slot and that slot does the database query.

You create an instance of QThread and an instance of this new class and then use moveToThread() to make it run in the worker thread.

The slot then gets invoked in the context of the worker thread.
_

Thanks for the reply
my problem is that , i have a class the represents the GUI , a button in it, when pressed it opens a dialog
now in this dialog i get the user input through line edit , and i have a button , when pressed i want to execute the query
so should i connect to the database in the main GUI ? or in the class subcalssing QObject ?
the other problem is that if i start the thread on pressing the button in the dialog , the thread will get destroyed before doing the query
where to create the intsnance of the class subclassing QObject ?
since my query takes parameters , i should pass them to the Thread. How to do that ? Signals and Slots are not doing the job properly in my case

anda_skoa
9th September 2013, 13:20
so should i connect to the database in the main GUI ? or in the class subcalssing QObject ?

I would do it in the qobject class, in the slot that executes the query or in a slot connected to the thread's started signal.



the other problem is that if i start the thread on pressing the button in the dialog , the thread will get destroyed before doing the query

Why would it? The default implementation of QThread::run() runs an event loop. it doesn't finish until QThread::exit() is called.



where to create the intsnance of the class subclassing QObject ?

When you create the thread. Since you want to directly connect the dialog to the query slot, before you create the dialog or even when you create the applicatIons' main GUI.



since my query takes parameters , i should pass them to the Thread. How to do that ? Signals and Slots are not doing the job properly in my case

Signals can transport parameters to the slot.
Does it work if you do not use a thread and have the query class instance in the main thread?

Cheers,
_

toufic.dbouk
9th September 2013, 14:29
Signals can transport parameters to the slot.
Does it work if you do not use a thread and have the query class instance in the main thread?
_

everything works fine without the use of threads.
i mean it doesnt work when i send the parameters through signals and slot and then save them as attributes in the QObject class , then when i try to use the attributes in the QsqlQuerry like binding it ( in the QObject class ) , the app crashes

i will try what you said concerning the threads even though i tried something close to that , and get back to you
i really appreciate your help. Thanks.

Added after 1:

gonna update you with what i have changed :

class X : Main GUI, when button pressed creates an instance of class Y ( dialog )
class Y : dialog that opens when a button pressed from CLASS X : *method onButtonPressed*( create instance of CLASS Z , call *method1 setup* , start the thread)
class Z sub-classing QObject : *method1 setup*( connecting the threads started SIGNAL to execute SLOT PLUS opening connection with database )
*method2 execute*( do query )

okay so first of all , the execute slot connected to the thread Started SIGNAL is never executed.
when i move the content of the execute method to the setup method , the code executes but the GUI still freezes.
the statement : query.exec(); takes around 20 sec to execute and that's when the whole GUI freezes .

toufic.dbouk
9th September 2013, 16:57
im connecting the start method of the thread in the QObject class ( CLASS Z in the previous post)

void QueryThread::setupThread(QThread &qryThread)
{
qDebug () << "method invoked";
connect(&qryThread,SIGNAL(started()),this,SLOT(executeQuery ()));
}

and im setting it up here in another class ( CLASS Y in the previous post)

QueryThread qryThread;
qryThread.setupThread(qThread);
qryThread.moveToThread(&qThread);
qThread.start();
i dont get why this isnt working ? the method is never invoked
NOTE: QThread qThread; is a public attribute in CLASS Y

anda_skoa
9th September 2013, 21:23
Does the qryThread object live long enough?

Here it appears to be created on the stack of some function.

Cheers,
_

toufic.dbouk
9th September 2013, 22:06
Does the qryThread object live long enough?

Here it appears to be created on the stack of some function.
_
i created it on heap and with a couple of other fixes problem got solved
i dont how i missed that...

but still why does the query.exec() take 20 sec or more to execute ? is there a fast way to retrieve a data from a 2 mill record database

why cant we great a GUI in the thread ? like if i want to just show a tableView

i wanna show the result of the query done by the thread in a GUI table
i was hoping to do somethinh simple like :

QSqlQueryModel * model = new QSqlQueryModel;
model->setQuery(Pqry);
QTableView *view = new QTableView;
view->setModel(model);
view->show();

anda_skoa
10th September 2013, 10:58
but still why does the query.exec() take 20 sec or more to execute ? is there a fast way to retrieve a data from a 2 mill record database

Always depends on the query and the database.
If you query something that is not indexed, it will take longer.
If your query contains instructions to sort the result, it will take longer.
Some databases are less optimal for huge datasets, some have different backends for different kinds of loads, etc.



why cant we great a GUI in the thread ? like if i want to just show a tableView

UI resources are usually not protected against concurrent access, some systems can't even do that natively.
Additional to that Qt will handle events in the thread that an object belongs to, so creating UI from different threads would lead to two threads processing user events.
Which would lead to highly unpredictable execution paths.



i wanna show the result of the query done by the thread in a GUI table
i was hoping to do somethinh simple like :

QSqlQueryModel * model = new QSqlQueryModel;
model->setQuery(Pqry);
QTableView *view = new QTableView;
view->setModel(model);
view->show();

You could try creating the model in the thread, then move it to the GUI thread and set it on the view there.
Might be necessary to wait with the move/set until the query is done.

Cheers,
_

toufic.dbouk
10th September 2013, 16:04
Thanks for the explanation it helps a lot.



You could try creating the model in the thread, then move it to the GUI thread and set it on the view there.
Might be necessary to wait with the move/set until the query is done.


i actually dont have enough practice background with threads so i don't how to do that...
any help from you would be way more than appreciated. such as more explanation or a reference or a code snippet

Best Regards.

Added after 17 minutes:

Hello anda_skoa,
i managed to show the result in the GUI thread by the following:
emit a signal holding the QSqlQuery after the QSqlQuery.exec() is done
catch it in the dialog Classs ( because thats where i create the instance of the class subclassing QObject " worker thread ")
send a signal from the dialog class holding the QSqlQuery to the GUI thread and set the result in a table view

this is working fine , not slow at all ...
but im not sure if that's the right way of doing this

i would like to hear from you on this and tell me the way you proposed because im sure it would be more efficient

You could try creating the model in the thread, then move it to the GUI thread and set it on the view there.
Might be necessary to wait with the move/set until the query is done.

Thanks a lot for your time and for your extremely helpful replies , i have learned a lot from this thread.

anda_skoa
10th September 2013, 16:53
Sounds fine to me, even better then my previous idea actually.

Cheers,
_

toufic.dbouk
10th September 2013, 17:09
Thank you so much for your time
its actually working great and fast without a single freeze
ill work more on it and ill get back to this thread or a new thread , if i need any further help
enjoy your day friend.

wysota
10th September 2013, 20:04
I don't know how internals of QSqlQuery work but I believe that for some backends the query can still call some database functions after it is executed (e.g. when iterating over results, next results can be fetched from the database on the fly) thus accessing it from a thread different than where the query was executed could result in undefined behaviour. I would be very careful about this.

toufic.dbouk
10th September 2013, 20:36
Hi Wysota,
im actually aware of that because i tried printing the result by iterating over the QSqlQuery when catching the emited signal by :

while(query.next())
{
qDebug() << query.value(2).toString();
}

but i guess im just accesing the sent result from the worker thread so i dont think you can do more than seeing the result with that query
thanks for pointing it out. and if you have any other idea on how to do that please let me know.
i know you posted the first reply on this thread :

Clone the database connection, open it, run your query, close and remove the connection. Everything apart cloning has to be done from the thread that is to execute the query.
but i dont how to do that so i went through the other option by anda_skoa which turned out to work properly and efficiently
if you explain about your way of doing that im ready to try it and get back to you on this post.
Best Regards.

wysota
11th September 2013, 01:53
and if you have any other idea on how to do that please let me know.
A custom model that performs sql queries in a thread (if needed), converts the data to the format operated by the model, transfers the data to the model's thread and updates the model. This is something you have to implement yourself, of course.


but i dont how to do that so i went through the other option by anda_skoa which turned out to work properly and efficiently
Today it works but from now on upon every crash or disfunction of your project you'll be wondering whether the database is causing that...

toufic.dbouk
11th September 2013, 20:57
Hello,


Today it works but from now on upon every crash or disfunction of your project you'll be wondering whether the database is causing that...
i will test the project , since its not a big project , i might be able to have a thorough testing and if it crashed or failed to function properly ill implement your way of doing it
which is quite good i guess in my case
Best Regards.