如何使用moveToThread()在pyqt中正确使用QThread? [英] How to use QThread correctly in pyqt with moveToThread()?

查看:1029
本文介绍了如何使用moveToThread()在pyqt中正确使用QThread?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我阅读了这篇文章

i read this article How To Really, Truly Use QThreads; The Full Explanation, it says instead of subclass qthread, and reimplement run(), one should use moveToThread to push a QObject onto QThread instance using moveToThread(QThread*)

这是c ++示例,但我不知道如何将其转换为python代码.

here is the c++ example, but i don't know how to convert it to python code.

class Worker : public QObject
 {
     Q_OBJECT
     QThread workerThread;

 public slots:
     void doWork(const QString &parameter) {
         // ...
         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,但是如您所见,它使用的是不推荐的方式.我该如何重写它以使用首选方法?

i've been using this method to generate a qthread , but as you can see, it's using the not recommended way. how can i re-write it to use the preferred method ?

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)

两年后... 我尝试了qris的代码,它可以在不同的线程中工作

edit: two years later ... I tried qris' code, it works and in different thread

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()实现为您运行一个事件循环,等效于:

The default run() implementation in QThread runs an event loop for you, the equivalent of:

class GenericThread(QThread):
    def run(self, *args):
        self.exec_()

事件循环的重要之处在于,它允许线程拥有的对象在其插槽上接收事件,事件将在该线程中执行.这些对象只是QObject,而不是QThreads.

The important thing about an event loop is that it allows objects owned by the thread to receive events on their slots, which will be executed in that thread. Those objects are just QObjects, not QThreads.

重要说明:QThread对象不归其自己的线程所有!它是在主线程上创建的,并在那里存在.除了其run方法之外,其所有代码都在主线程中执行.

Important note: the QThread object is not owned by its own thread! It was created on the main thread and lives there. Apart from its run method, all of its code executes in the main thread.

因此您应该能够做到这一点:

So you should be able to do this:

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()方法在完成后发出该信号,并将结果传递给它.

Also, think carefully about what happens with the result of self.function, which is currently discarded. You could declare another signal on GenericWorker, which receives the result, and have the run() method emit that signal when it's done, passing the result to it.

一旦掌握了这些要点,并且意识到您不应该也不应该继承QThread,生活就会变得更加简单明了.简而言之,永远不要在QThread中工作.您几乎应该永远不需要覆盖运行.对于大多数使用情况,设置与QObject到QThread的适当关联并使用QT的信号/插槽为进行多线程编程提供了一种非常强大的方法.请注意不要让您推送到工作线程的QObject徘徊...

Once you get the hang of it and realize you don't and shouldn't subclass QThread, life becomes a lot more straightforward and easier. Simply put, never do work in QThread. You should almost never need to override run. For most use cases, setting up proper associations with a QObject to a QThread and using QT's signals/slots creates an extremely powerful way to do multithreaded programming. Just be careful not to let the QObjects you've pushed to your worker threads hang around...

http://ilearnstuff.blogspot.co.uk/2012 /09/qthread-best-practices-when-qthread.html

这篇关于如何使用moveToThread()在pyqt中正确使用QThread?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆