PDA

View Full Version : How to Have a class run in a separate thread.



sona1111
5th August 2013, 02:24
This question is related to Pyside 4

I am trying to accomplish something that at first seemed like a common task but maybe I am overlooking some technical problem with it.

I have a simple class for "database". The init has a few lines of code for connecting to the database, and then the class has various functions for standard read and write operations and a few special cases etc. In the past, I would initialize the database class, and then just use its functions to accomplish basic IO. The issue is that the database connection in the init, as well as the IO methods, can take a second or two, leaving the main GUI frozen. In the past I have used the qthread class for this, and placed code in the run() method for either a one-shot running of the code, or a periodic (once a minute or something) running of some code. In this case, though, I want the thread to just run as long as the instance of the class is running. Or to be more specific, the thread should run for initialization of the database class as well as whenever I call a method of the database class. I have searched for this and found suprisingly few results for something that seems like it would be a common desire. (to have one instance run as a thread)

Thanks.

karankumar1609
5th August 2013, 06:35
This question is related to Pyside 4

I am trying to accomplish something that at first seemed like a common task but maybe I am overlooking some technical problem with it.

I have a simple class for "database". The init has a few lines of code for connecting to the database, and then the class has various functions for standard read and write operations and a few special cases etc. In the past, I would initialize the database class, and then just use its functions to accomplish basic IO. The issue is that the database connection in the init, as well as the IO methods, can take a second or two, leaving the main GUI frozen. In the past I have used the qthread class for this, and placed code in the run() method for either a one-shot running of the code, or a periodic (once a minute or something) running of some code. In this case, though, I want the thread to just run as long as the instance of the class is running. Or to be more specific, the thread should run for initialization of the database class as well as whenever I call a method of the database class. I have searched for this and found suprisingly few results for something that seems like it would be a common desire. (to have one instance run as a thread)

Thanks.

hello sona,

You can use QThread for this kind of problem.
If your database class is a separate class, the you can use moveToThread



class *a = new class;
QThread *thread = new QThread;
a->moveToThread(thread);
thread->start();


the above code will now run the object a in a different thread.

or you can create your own thread by using QThread

refer http://doc.qt.digia.com/4.6/threads-modules.html#threads-and-the-sql-module

anda_skoa
5th August 2013, 13:01
Or do what you have done so far and just don't exit from run().

Either using a loop or calling exec() to start the thread's event loop.

Cheers,
_

sona1111
5th August 2013, 16:52
So it still lags when connecting and retrieving data using move to thread even though with move to thread following this example for my program almost line by line: http://stackoverflow.com/questions/11265812/pyside-pyqt-starting-a-cpu-intensive-thread-hangs-the-whole-application

(as it is the only example of this I can find)

However, getting the thread id in the DB class and the main class yields the same ID, so I must be doing something wrong. Does anyone have a small python example of movetothread?

thanks.

sona1111
24th August 2013, 17:42
I still have had no luck getting this to work. In all examples I can find, the only part of the object that runs in a thread is a single method, not other parts of the class. For example in the code from SO:



from PySide import QtCore
import time
import sys


# Subclassing QObject and using moveToThread
# http://labs.qt.nokia.com/2007/07/05/qthreads-no-longer-abstract/
class SomeObject(QtCore.QObject):

finished = QtCore.Signal()

def longRunning(self):
count = 0
while count < 5:
time.sleep(1)
print "Increasing"
count += 1
self.finished.emit()






def usingMoveToThread():
app = QtCore.QCoreApplication([])
objThread = QtCore.QThread()
obj = SomeObject()
obj.moveToThread(objThread)
obj.finished.connect(objThread.quit)
objThread.started.connect(obj.longRunning)
objThread.finished.connect(app.exit)
objThread.start()
sys.exit(app.exec_())



if __name__ == "__main__":

usingMoveToThread()

This is really only calling the "longrunning" method in another thread, and other operations that I could call on the class would seem to be required to call "start" on the class again and make another instance, then call another method in a separate thread which for the database would mean calling the constructor again and reconnecting to the database again. I don't mind if database operations get queued in the worker thread, actually that would be preferable because the throughput to a single DB is not going to increase be trying to connect twice anyway. I just want to use the DB instance with values it returns going to the GUI when it is done pulling the data (without freezing the GUI during the network operations). Because I already had the class/object set up correctly to work I thought it would make sense to do it this way but maybe what I am saying is impossible. Here is an example of what I am looking for:

Suppose I have this class (hastily written so it might not be completely perfect):


class DBcore(QtCore.QObject):


def __init__(self, parent=None):
super(DBcore, self).__init__(parent)
connection = pymongo.MongoClient(connection_string)
database = connection.avdb
self.db = database



def showDatabaseTable(self, tableToLoad):

for q, row in enumerate(self.requests.find()):
table_rows += 1
if q == 0:
for col in row:
table_cols += 1
time.sleep(0)
else:
pass

self.retr_table_cols = table_cols
self.retr_table_rows = table_rows
self.retr_data = self.requests.find().sort([('_id',1)])


return (self.retr_table_cols, self.retr_table_rows, self.retr_data)

In my GUI object or somewhere in a higher level, I would put "DB = DBcore()" and then either use the functions like DB.showDatabaseTable(clients) or just use mongoDB in a random write or something like DB.db.clients.insert({"name":"John"}). In both of these cases I would like the operation to run in a separate thread automatically, because it is using the "DB" object. Is this possible or unheard of?

Thanks.

wysota
27th August 2013, 09:24
"Classes" don't "run in threads". Classes are just code. If you call some function from thread A it will run in thread A, if you call it from thread B, it will run in thread B. When QThread is started, it calls its run() method in a new thread, that's why "run" executes in context of that thread. If you want other methods to be ran in that thread, call them from run() or use signal-slot connections, move the object to the desired thread and make sure the event loop is running in that thread. Then all slots when invoked as such will be ran in the context of the thread owning the object.

sona1111
28th August 2013, 23:59
I am having trouble understanding how the "event loops" are supposed to run in a similar manner to classes. (note for this post and my previous one, I am well aware that the code in a class does not do anything by itself, when I say "class" in this case, I mean one instance of an object from the class I wrote. I thought this was a common concept, but maybe calling it a class instead of an object confused people.) For example, In the above class example, I have a handle on the DB object (an instance created from the mongodb 'driver'), anytime I call an operation on this, I would like it to run in a separate thread. (I am making it simpler so maybe it will be easier to understand) First of all, what does an "event loop" really mean? Is it explained somewhere? Is it just one literal while loop which runs until you kill the thread? If it is run like that, how are you supposed to call the methods on the DB object from the GUI? are you supposed to emit a signal with a string and try to think of every possible method you will call and define if/then's for it? (if so, this is a ridiculous way to program anything) Are you supposed to emit a signal with a large amount of strings and use a bunch of getattr statements to append them to the DB object? It still sounds like the wrong way to go.

If I am still missing something major here, please tell me. Otherwise, can anyone make a demo for this? A single window with a few buttons that call DB.insert, DB.find etc when you press them, but do not freeze the gui when you click them.

Thank you for reading.

wysota
29th August 2013, 06:44
I am having trouble understanding how the "event loops" are supposed to run in a similar manner to classes.
They are not since classes (and objects) do not "run" in any way. The only thing that an object does is that it "exists".

An event loop is basically this code:


while(!stopCondition) {
processEvents();
}

with "processEvents" being a call which takes an event out of a queue and processes it.

You can compare "objects" to books and threads to people reading books. If you have two books, it is not true that there are separate people reading them just because they are different books. When one book has a reference to another book, reading that book does not require a new person -- the initial person just puts the original book aside, takes the other one, reads what needs to be read, puts that book aside and comes back to the first one. In this situation the process of reading the first book is "frozen" for the duration of reading the other book. The same applies if the book refers to itself -- you open the book on a different page, read what you need and go back to the previous context. The basic principle is that you can read only one line of text from one book at a time and that you read lines in sequence or can jump to a different line and start reading there.

With multiple people (multiple threads) they can read two books at the same time or they even can read different pages of the same book at the same time (however with the latter, they have to be more careful not to disturb each other) or even read the same lines of the same book concurrently provided if some conditions are met (they can both see the text and they "take notes" of the content on different sheets of paper instead of say... writing on the margins of the book directly).

If you read book A and you want some information from book B but you want to save some time and continue reading book A, then it doesn't happen automatically that your friend reads book B for you and hands over his notes related to what you need from the other book. You have to tell the guy "hey my good friend, could you read page 42 from book B for me and tell me what it is about?" thus you send a message to your friend asking him for a favour. If you do that in a wrong moment you might disturb your friend the same way as he would make you very angry if he tried to give you the information you needed while you were taking your own notes and you forgot half of what you wanted to write because of him talking to you or worse writing down his answers on your piece of paper over the text you have already written. You and your friend need to synchronize how you exchange information.

This is exactly the same with threads. If you want a worker thread to do something for you then you need to ask for it and when the response is ready the other thread has to know how to return that info to you. Qt provides two built-in mechanisms for that -- events and signals and slots and apart from those two you can build your own mechanisms based on direct data exchange provided you do proper synchronization using synchronization primitives such as mutexes and wait conditions.

Basically you will either have to wait until your friend finishes reading what you wanted him to read for you (which usually defeats the whole purpose of cooperating with a different "person" as you might have as well read that other book yourself then) or you need a way to be notified about the data when it is ready.