Как перемещать объект между QThreads в Pyqt и обратно

В моей программе (с использованием Python 2.7) я создаю объект, содержащий некоторые важные данные и методы. Некоторые методы потребляют много ресурсов ЦП, поэтому в некоторых случаях я перемещаю объект в новый QThread на время выполнения методов, интенсивно использующих ЦП, а затем возвращаю их в основной поток. Позже, когда вызывается метод с интенсивным использованием ЦП, я хотел бы снова переместить объект в другой QThread, однако это не позволяет сказать: «Текущий поток не является потоком объекта».

Вот тривиальный пример, который воспроизводит проблему:

import sys
from PyQt4 import QtCore, QtGui
from time import sleep

class ClassA(QtGui.QDialog):
    def __init__(self):
        super(ClassA, self).__init__()
        mainLayout=QtGui.QVBoxLayout()
        self.lineEdit=QtGui.QLineEdit()
        mainLayout.addWidget(self.lineEdit)
        self.setLayout(mainLayout)
        self.show()
        self.obj=ClassC(self)        
        self.executeProgram()
    def executeProgram(self):
        self.lineEdit.setText("Starting new thread...")
        self.thread=QtCore.QThread()
        self.obj.moveToThread(self.thread)        
        self.thread.started.connect(self.obj.doWork)
        self.obj.doingWork.connect(self.updateGui)
        self.obj.finished.connect(self.killThread)
        self.thread.start()
    def updateGui(self,message):
        self.lineEdit.setText(message)
    def killThread(self):
        self.thread.quit()
        self.thread.wait()
        self.obj.finished.disconnect()
        self.executeProgram()

class ClassC(QtCore.QObject):
    finished=QtCore.pyqtSignal()
    doingWork=QtCore.pyqtSignal(str)
    def __init__(self,parent=None):
            super(ClassC, self).__init__()
    def doWork(self):
        for i in range(5):
            self.doingWork.emit("doing work: iteration "+str(i))
            sleep(1)
        self.finished.emit()

if __name__=="__main__":
    app=QtGui.QApplication(sys.argv)
    obj=ClassA()
    app.exec_()       

Можно ли несколько раз перемещать объект в другой QThread? Если да, то как мне исправить мой код, чтобы сделать это?


person Tim Rae    schedule 11.05.2013    source источник


Ответы (1)


Примечание. moveToThread должен вызываться в потоке, которому в данный момент принадлежит объект, поэтому вам может потребоваться переместить объект обратно в основной поток, прежде чем переместить его в еще один поток.

Добавьте строку mainThread = QtCore.QThread.currentThread() где-нибудь вверху и поместите self.moveToThread(mainThread) прямо перед отправкой сигнала finished.

person Benjamin Peterson    schedule 11.05.2013
comment
Привет Бенджамин, спасибо за ваш ответ. Класс B действительно выполняется в другом потоке. Переопределение метода run для QThread не требуется и больше не рекомендуется по QT. - person Tim Rae; 12.05.2013
comment
Нет, это не так. (Если вы не уверены, попробуйте напечатать значение QThread.currentThreadId() в нескольких местах.) Созданный вами QThread живет в основном потоке, поэтому, когда он отправляет сигнал started, он отправляется в основном потоке. Вам нужно использовать другой сигнал или метод invokeMethod, предложенный по этой ссылке. - person Benjamin Peterson; 12.05.2013
comment
Я отредактировал код, чтобы показать, что он работает (извините, мне пришлось установить соединение после moveToThread()). - person Tim Rae; 12.05.2013
comment
Большое спасибо, я предполагал, что поток будет автоматически перемещен обратно в родительский поток после выхода из рабочего потока, но это явно не так. Добавление self.moveToThread(QtGui.QApplication.instance().thread()) непосредственно перед испусканием готового сигнала также работает! - person Tim Rae; 12.05.2013
comment
Для пользователей C++/Qt: this->moveToThread(QApplication::instance()->thread()); - person inblueswithu; 05.04.2014
comment
Для меня использование QtGui.QApplication.instance().thread() не сработало, но сохранение QThread.currentThread() ранее работало отлично. Спасибо @BenjaminPeterson! - person phyatt; 01.01.2015