QThread 在 Python 中的使用

QThread use in Python

我在 Python 中编写更大的程序是相当新的(我以前只写过短脚本)。我将要编写的程序从外部设备接收数据,将其保存到数据库并在 GUI 中显示(连续)。由于处理数据很耗时,我想使用线程(具体来说是 PySide QtCore QThreads)。我能想到的最好的方法是设置两个线程,一个用于数据库进程,一个用于处理串行端口,在 MainThread 中使用 GUI 运行ning。现在我已经阅读了一大堆关于 proper/improper QThreading 的东西,从 Maya's post and digging deeper, up to this post 开始,我发现:

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.

问题 1:现在我不知道的第一件事是要走哪条路(subclassing 与否)。我的猜测是 subclassing,因为(问题 2),但我不完全确定,现在我坚持 moveToThread().

问题 2:我不知道的另一件事是如何使线程中的操作成为事件驱动的(SerialThread 从设备接收数据,通过信号将其发送到 DatabaseThread,DatabaseThread 收集数据并将其放入数据库)。

问题 3:当使用 moveToThread() 时,我为 class 编写的每个方法都会得到 AttributeError: 'PySide.QtCore.QThread' object has no attribute 'my_awesome_method'。我很清楚,我不明白这里的操作原理。如何实现我希望 my_class 在使用 my_class.moveToThread(my_thread) 时拥有的方法?

我找到了所有关于 QThreads 和 C++ 的教程,所以这个 discussion on QThreads in Python 很有趣,但没有解释我想知道的一切。我需要一个简单的示例,其中包含信号、槽以及关于如何使用 运行() 的准确解释。我装饰它吗?为什么?如何?它是如何工作的?

我的代码的简短版本如下所示:

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()

我的数据库和序列号 class 看起来有点像这样:

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

问题 1

我认为最好的方法是最简单的:不要子类化,只创建两个不同的线程。第一个是移动数据库对象,第二个是串行对象。这样一来,你的子类线程的实现就不会出错,修复bug也会更快。


问题 2

我不知道您的应用程序的体系结构,但您可以在创建线程并在其中移动工作对象后执行以下操作:

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()

其实QThreads的好处就是不真正去修改代码


问题 3

我认为问题在于您正在尝试使用 QThread 调用 my_awesome_method,您的数据库或串行侦听器已移至该位置。相反,您应该用对象本身调用方法,就好像它不在不同的线程中一样!

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

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