私はこの記事を読みました 実際にQThreadsを実際に使用する方法;完全な説明 、サブクラスqthreadの代わりに、run()を再実装すると、moveToThread(moveToThread()を使用してQObjectをQThreadインスタンスにプッシュする必要があります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)
編集:2年後...私は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,
QThreadのデフォルトのrun()実装は、次と同等のイベントループを実行します。
_class GenericThread(QThread):
def run(self, *args):
self.exec_()
_
イベントループの重要な点は、オブジェクト所有者スレッドがスロットでイベントを受信できるようにすることです。イベントは実行されますそのスレッド内。これらのオブジェクトは単なるQObjectであり、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のシグナル/スロットを使用すると、マルチスレッドプログラミングを行うための非常に強力な方法が作成されます。ワーカースレッドにプッシュしたQObjectがぶら下がらないように注意してください...
http://ilearnstuff.blogspot.co.uk/2012/09/qthread-best-practices-when-qthread.html
アプリケーションでqrisの例を使用しようとしましたが、メインスレッドでコードを実行し続けました。それは彼がrunと呼ぶことを宣言したsignalの方法です!
基本的に、オブジェクトのコンストラクターで接続すると、2つのオブジェクト間に接続が存在しますメインスレッド内-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")
@qrisと@MatthewRuncheyの両方のアプローチを試しました。
あり@pyqtSlot
デコレータQtは、シグナルが発行されるときにワーカーインスタンスの「場所」をチェックします。moveToThread
がワーカースレッドのスロットを実行した後、moveToThread
がシグナルを発行する前に接続が確立された場合でも。
なし@pyqtSlot
デコレータQtは、接続が確立された瞬間にワーカーインスタンスの「場所」をフリーズします。それがmoveToThread
の前だった場合、メインスレッドにバインドされ、スロットコードはメインで実行され続けます。 moveToThread
呼び出しの後に信号が発行された場合でもスレッド。
確立された接続aftermoveToThread
は、どちらの場合もワーカースレッドを実行するスロットをバインドします。
コード:
from PyQt5.QtCore import (QCoreApplication, QObject, QRunnable, QThread,
QThreadPool, pyqtSignal, pyqtSlot)
class Worker(QObject):
def __init__(self):
super(Worker, self).__init__()
# self.call_f1.connect(self.f1)
# self.call_f2.connect(self.f2)
call_f1 = pyqtSignal()
call_f2 = pyqtSignal()
@pyqtSlot()
def f1(self):
print('f1', threading.get_ident())
@pyqtSlot()
def f2(self):
print('f2', threading.get_ident())
app = QCoreApplication([])
print('main', threading.get_ident())
my_thread = QThread()
my_thread.start()
my_worker = Worker()
my_worker.call_f1.connect(my_worker.f1)
my_worker.call_f1.emit()
my_worker.moveToThread(my_thread)
my_worker.call_f2.connect(my_worker.f2)
my_worker.call_f1.emit()
my_worker.call_f2.emit()
sys.exit(app.exec_())
デコレータ付き:
main 18708
f1 18708
f1 20156
f2 20156
デコレータなし:
main 5520
f1 5520
f1 5520
f2 11472
PSワーカーでの接続__init__
メソッドは、メインスレッドでmoveToThread
の前に接続することと明らかに同等です。
(PyQt5、win64でテスト済み)。