1

I am fairly new in writing bigger programs in Python (I was only writing short scripts before). The program I'm about to write recives data from an external device, saves it to database and displays it in GUI (continuously). Since handling the data is time consuming I want to use threads (PySide QtCore QThreads to be specific). The best way to do this that I can think of is to set up two threads, one for the database processes and one for the handling of serial port, with GUI running in the MainThread. Now I've read a whole bunch of stuff about proper/improper QThreading, starting with Maya's post and digging deeper, up to this post where I found that:

When to subclass and when not to?

If you do not really need an event loop in the thread, you should subclass. If you need an event loop and handle signals and slots within the thread, you may not need to subclass.

Question 1: Now the first thing I don't know is which way to go (subclassing or not). My guess is subclassing, since (Question2), but I'm not entirely sure and for now I'm sticking to moveToThread().

Question 2: The other thing I have no clue about is how to make the actions in my threads event-driven (the SerialThread receives the data from the device, sends it via signal to the DatabaseThread, the DatabaseThread collects the data and puts it in the database).

Question 3: When using moveToThread() I get AttributeError: 'PySide.QtCore.QThread' object has no attribute 'my_awesome_method' for every method I write for my class. It's clear to me that I don't understand the principle of operation here. How do I implement my methods that I want my_class to have while using my_class.moveToThread(my_thread)?

I've found all tutorials possible on QThreads and C++, so this discussion on QThreads in Python is interesting, but does not explain everything I want to know. I need a simple example with signals, slots and an exact explanation on how to use run(). Do I decorate it? Why? How? How does it work?

The short version of my code looks like this:

from PySide import QtCore
from app.thrads.gui.gui import Gui            #my own class
from app.threads.db.db import Database        #my own class
from app.threads.serial.serial import Serial  #my own class

class AppManager():
    def __init__(self):
        self.gui = Gui()

        self.db = Database()
        self.db_thread = QtCore.QThread()
        self.db.moveToThread(self.db_thread)
        self.db_thread.start()

        self.serial = Serial()
        self.serial_thread = QtCore.QThread()
        self.serial.moveToThread(self.serial_thread)
        self.serial_thread.start()

and my Database and Serial class look somewhat like this:

from PySide import QtCore
from .db_signal import DbSignal

class Database(QtCore.QObject):
    def __init__(self):
        super(Database, self).__init__()
        self.signal = DbSignal()

    def my_awesome_method(self):
        ''' does something awesome '''
        pass
Community
  • 1
  • 1

1 Answers1

0

Question 1

I think the best way is the easiest: don't subclass, just create two different threads. In the first, move the Database object, in the second the Serial one. As it, you won't make mistakes in the implementation of your sub-classed threads,and bug fixing will be quicker.


Question 2

I don't know the architecture of your application, but you could do as follows, after creating the threads and moving the worker objects in them:

self.serial_thread.started.connect(self.serial.start)
self.serial_thread.finished.connect(self.serial.stop)

self.db_thread.started.connect(self.db.start)
self.serial_thread.finished.connect(self.db.stop)

self.serial.data_ready.connect(self.db.handle_new_data)

# Starting the database first, as it we won't lose a single packet of data
self.db_thread.start()
self.serial_thread.start()

In fact, the advantage of QThreads is that they doesn't really modify the code.


Question 3

I think the problem is you're trying to call my_awesome_method with the QThread where your data base or your serial listener has been moved to. On the contrary, you should call the method with the object itself, as if it wasn't in a different thread !

# Bad!
obj.moveToThread(thread)
thread.method_of_obj(param)

# Good!
obj.moveToThread(thread)
obj.method_of_obj(param)
Spirine
  • 1,837
  • 1
  • 16
  • 28
  • Thank you (: **Q1:** my not-so-experienced intuition told me the same, but I thought the other option cannot be popular without a reason. I'm happy to get my dilemma solved! **Q2:** how does that work since the only function that 'lives' in the new thread is run()? How do I tell run() to execute my code? **Q3:** yes, that was my mistake, thanks! – AerialTriceratops Apr 29 '15 at 11:06
  • @AerialTriceratops **Q2:** you should've read the [Qt5 documentation](http://doc.qt.io/qt-5/qthread.html#details)... ;) in short, you add QObject in a QThread, and then call the QThread.start method. It starts the execution of the thread, by calling the run() method. Also, it runs the event loop; thus, you the signals / slots now works. – Spirine Apr 29 '15 at 12:24
  • I've read it before, now I have to understand it :P Thanks a lot for pointing me in the right direction! I'm sorry that I cannot upvote your answer with my 6 reputation points :( – AerialTriceratops Apr 29 '15 at 13:10