Как правильно использовать QThread в pyqt с moveToThread ()?
Я читал эту статью Как Действительно, Действительно Использовать QThreads; Полное Объяснение, он говорит вместо подкласса qthread и reimplement run (), следует использовать moveToThread для нажатия QObject на экземпляр QThread с помощью moveToThread(QThread*)
вот пример C++, но я не знаю, как преобразовать его в код python.
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
public slots:
void doWork(const QString ¶meter) {
// ...
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
я использовал этот метод для создания qthread , но, как видите, это не рекомендуемый способ. как могу ли я переписать его, чтобы использовать предпочтительный метод ?
class GenericThread(QThread):
def __init__(self, function, *args, **kwargs):
QThread.__init__(self)
# super(GenericThread, self).__init__()
self.function = function
self.args = args
self.kwargs = kwargs
def __del__(self):
self.wait()
def run(self, *args):
self.function(*self.args, **self.kwargs)
edit: два года спустя ... Я пробовал код qris, он работает и в разных потоках
import sys
import time
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import pyqtSignal, pyqtSlot
import threading
def logthread(caller):
print('%-25s: %s, %s,' % (caller, threading.current_thread().name,
threading.current_thread().ident))
class MyApp(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 280, 600)
self.setWindowTitle('using threads')
self.layout = QtGui.QVBoxLayout(self)
self.testButton = QtGui.QPushButton("QThread")
self.testButton.released.connect(self.test)
self.listwidget = QtGui.QListWidget(self)
self.layout.addWidget(self.testButton)
self.layout.addWidget(self.listwidget)
self.threadPool = []
logthread('mainwin.__init__')
def add(self, text):
""" Add item to list widget """
logthread('mainwin.add')
self.listwidget.addItem(text)
self.listwidget.sortItems()
def addBatch(self, text="test", iters=6, delay=0.3):
""" Add several items to list widget """
logthread('mainwin.addBatch')
for i in range(iters):
time.sleep(delay) # artificial time delay
self.add(text+" "+str(i))
def test(self):
my_thread = QtCore.QThread()
my_thread.start()
# This causes my_worker.run() to eventually execute in my_thread:
my_worker = GenericWorker(self.addBatch)
my_worker.moveToThread(my_thread)
my_worker.start.emit("hello")
# my_worker.finished.connect(self.xxx)
self.threadPool.append(my_thread)
self.my_worker = my_worker
class GenericWorker(QtCore.QObject):
start = pyqtSignal(str)
finished = pyqtSignal()
def __init__(self, function, *args, **kwargs):
super(GenericWorker, self).__init__()
logthread('GenericWorker.__init__')
self.function = function
self.args = args
self.kwargs = kwargs
self.start.connect(self.run)
@pyqtSlot()
def run(self, *args, **kwargs):
logthread('GenericWorker.run')
self.function(*self.args, **self.kwargs)
self.finished.emit()
# run
app = QtGui.QApplication(sys.argv)
test = MyApp()
test.show()
app.exec_()
вывод такой:
mainwin.__init__ : MainThread, 140221684574016,
GenericWorker.__init__ : MainThread, 140221684574016,
GenericWorker.run : Dummy-1, 140221265458944,
mainwin.addBatch : Dummy-1, 140221265458944,
mainwin.add : Dummy-1, 140221265458944,
mainwin.add : Dummy-1, 140221265458944,
mainwin.add : Dummy-1, 140221265458944,
mainwin.add : Dummy-1, 140221265458944,
mainwin.add : Dummy-1, 140221265458944,
mainwin.add : Dummy-1, 140221265458944,
2 ответов
реализация run() по умолчанию в QThread запускает цикл событий для вас, эквивалент:
class GenericThread(QThread):
def run(self, *args):
self.exec_()
важная вещь о цикле событий заключается в том, что он позволяет объектам принадлежащий поток для получения событий на своих слотах, которые будут выполняться в этой теме. Эти объекты-просто QObjects, а не QThreads.
важное примечание: объект QThread не принадлежит собственной нити! Он был создан на главной нить и живет там. Помимо метода run, весь его код выполняется в основном потоке.
так что вы должны быть в состоянии сделать это:
class GenericWorker(QObject):
def __init__(self, function, *args, **kwargs):
super(GenericWorker, self).__init__()
self.function = function
self.args = args
self.kwargs = kwargs
self.start.connect(self.run)
start = pyqtSignal(str)
@pyqtSlot
def run(self, some_string_arg):
self.function(*self.args, **self.kwargs)
my_thread = QThread()
my_thread.start()
# This causes my_worker.run() to eventually execute in my_thread:
my_worker = GenericWorker(...)
my_worker.moveToThread(my_thread)
my_worker.start.emit("hello")
кроме того, подумайте о том, что происходит с результатом self.function
, которые в настоящее время отменены. Вы можете объявить другой сигнал на GenericWorker
, который получает результат, и есть run()
метод испускает этот сигнал, когда он сделан, передавая ему результат.
как только вы получите повесить его и поймите, что вы не должны и не должны подкласс QThread, жизнь становится намного проще и проще. Проще говоря, никогда не работайте в QThread. Вам почти никогда не нужно переопределять run. В большинстве случаев настройка правильных ассоциаций с QObject для QThread и использование сигналов/слотов QT создает чрезвычайно мощный способ многопоточного программирования. Просто будьте осторожны, чтобы не позволить qobjects, которые вы подтолкнули к рабочим потокам, зависнуть вокруг...
http://ilearnstuff.blogspot.co.uk/2012/09/qthread-best-practices-when-qthread.html
Я пытался использовать пример qris в своем приложении, но продолжал запускать мой код в моем основном потоке! Это путь сигнал что он заявил, чтобы вызвать run!
в основном, когда вы соединяете его в конструкторе объекта, соединение будет существовать между двумя объектами в основном потоке-потому что свойства QObject принадлежат потоку, который их создал. При перемещении QObject в новый поток,в соединение не движется с вами. Уберите линию, которая соединяет ваш сигнал с функцией run, и подключите ее после перемещения рабочего в новый поток!
соответствующее изменение из ответа qris:
class GenericWorker(QObject):
def __init__(self, function, *args, **kwargs):
super(GenericWorker, self).__init__()
self.function = function
self.args = args
self.kwargs = kwargs
start = pyqtSignal(str)
@pyqtSlot
def run(self, some_string_arg):
self.function(*self.args, **self.kwargs)
my_thread = QThread()
my_thread.start()
# This causes my_worker.run() to eventually execute in my_thread:
my_worker = GenericWorker(...)
my_worker.moveToThread(my_thread)
my_worker.start.connect(my_worker.run) # <---- Like this instead
my_worker.start.emit("hello")