PySide QThread moveToThread子类锁定主线程

时间:2017-06-27 18:42:55

标签: python-3.x pyside

当我尝试将我的工作类子类化为主UI线程锁定并且在工作线程完成之前变得无响应时,我遇到了问题。我在这里不知所措,怀疑GIL如何与继承相互作用?

MainWindow.clicked()这两行:

#self.worker = Worker() # This works
self.worker = SubWorker() # This locks up main thread

以下是我重现此内容的示例代码:

from PySide import QtGui, QtCore
import sys
import time

class Worker(QtCore.QObject):
    started = QtCore.Signal()
    progress = QtCore.Signal(int)
    finished = QtCore.Signal(bool)

    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)
        self.lock = QtCore.QMutex()

    @QtCore.Slot()
    def start(self):
        self.flag_to_quit = False
        try:
            self.started.emit()
            time.sleep(5)
            time.sleep(0.5)
            for i in range(10):
                self.progress.emit(i)
                time.sleep(1)

                with QtCore.QMutexLocker(self.lock):
                    print(self.flag_to_quit)
                    if self.flag_to_quit:
                        raise RuntimeError("Requested to quit.")
            self.finished.emit(True)
        except Exception as ex:
            print(ex)
            self.finished.emit(False)
        finally:
            print("start() finished!")

    #@QtCore.Slot()
    def stop(self):
        print("Stopping...")
        with QtCore.QMutexLocker(self.lock):
            self.flag_to_quit = True

    @QtCore.Slot()
    def __quit_handler(self):
        self.flag_to_quit = True


class SubWorker(Worker):
    def __init__(self):
        Worker.__init__(self)


class MainWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setLayout(QtGui.QVBoxLayout())

        self.label = QtGui.QLabel("")
        self.layout().addWidget(self.label)

        self.button = QtGui.QPushButton("Start Thread")
        self.layout().addWidget(self.button)
        self.button.clicked.connect(self.clicked)

    def closeEvent(self, event):
        if hasattr(self, "_thread") and self._thread:
            self.worker.flag_to_quit = True
            self._thread.quit()
            print("waiting for stop...")
            self._thread.wait()
            self._thread.deleteLater()

    @QtCore.Slot()
    def worker_started(self):
        self.label.setText("Started!")

    @QtCore.Slot(int)
    def worker_progress(self, interval):
        self.label.setText(str(interval))

    @QtCore.Slot()
    def worker_finished(self, success):
        msg = "Finished "
        msg += "successfully!" if success else "with errors" 
        self.label.setText(msg)

    @QtCore.Slot()
    def clicked(self):
        if hasattr(self, "_thread") and self._thread:
            self.worker.stop()
            self._thread.quit()
            print("waiting for stop...")
            self._thread.wait()
            self._thread.quit()
            self._thread.deleteLater()
        self._thread = QtCore.QThread()
        #self.worker = Worker() # This works
        self.worker = SubWorker() # This locks up main thread
        self.worker.started.connect (self.worker_started, QtCore.Qt.QueuedConnection)
        self.worker.progress.connect(self.worker_progress, QtCore.Qt.QueuedConnection)
        self.worker.finished.connect(self.worker_finished, QtCore.Qt.QueuedConnection)
        self.worker.moveToThread(self._thread)
        self._thread.started.connect(self.worker.start)
        self._thread.finished.connect(self.worker.stop)
        self._thread.start()


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

    main_window = MainWindow()
    main_window.show()

    app.exec_()
    sys.exit()

版本信息:

Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 19:28:18) [MSC v.1600 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.

编辑:

看起来QThread :: moveToThread()失败了Subclasses。这是QtCore.QThread.currentThread()在各个地方的输出:

#self.worker = Worker():

Main:<PySide.QtCore.QThread object at 0x7ff89df384d0>
Real thread: <PySide.QtCore.QThread object at 0x7ff89df383f8>
Worker:<PySide.QtCore.QThread object at 0x7ff89df384d0>



#self.worker = SubWorker()

Main:<PySide.QtCore.QThread object at 0x7f968602c3b0>
Real thread: <PySide.QtCore.QThread object at 0x7f968602c488>
Worker:<PySide.QtCore.QThread object at 0x7f968602c488>

1 个答案:

答案 0 :(得分:0)

好吧,我设法通过继承QThread并覆盖run方法,以“错误”的方式解决这个问题。我不确定为什么或如何但它有效。

from PySide import QtGui, QtCore
import sys
import time

class Worker(QtCore.QThread):
    _started = QtCore.Signal()
    progress = QtCore.Signal(int)
    _finished = QtCore.Signal(bool)

    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.lock = QtCore.QMutex()

    @QtCore.Slot()
    def run(self):
        self.flag_to_quit = False
    print("Worker:" + str(QtCore.QThread.currentThread()))
        try:
            self._started.emit()
            time.sleep(2)
            for i in range(10):
                self.progress.emit(i)
                time.sleep(1)
                with QtCore.QMutexLocker(self.lock):
                    if self.flag_to_quit:
                        raise RuntimeError("Requested to quit.")
            self._finished.emit(True)
        except Exception as ex:
            print(ex)
            self._finished.emit(False)
        finally:
            print("start() finished!")

    @QtCore.Slot()
    def stop(self):
        with QtCore.QMutexLocker(self.lock):
            self.flag_to_quit = True

    @QtCore.Slot()
    def __quit_handler(self):
        self.flag_to_quit = True


class SubWorker(Worker):
    def __init__(self, parent=None):
        Worker.__init__(self, parent)


    @QtCore.Slot()
    def start(self):
        Worker.start(self)


class MainWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setLayout(QtGui.QVBoxLayout())

        self.label = QtGui.QLabel("")
        self.layout().addWidget(self.label)

        self.button = QtGui.QPushButton("Start Thread")
        self.layout().addWidget(self.button)
        self.button.clicked.connect(self.clicked)

    def closeEvent(self, event):
    self.hide()
        if hasattr(self, "worker") and self.worker:
        self.worker.stop()
        self.worker.quit()
        self.worker.wait()
        self.worker.deleteLater()

    @QtCore.Slot()
    def worker_started(self):
        self.label.setText("Started!")

    @QtCore.Slot(int)
    def worker_progress(self, interval):
        self.label.setText(str(interval))

    @QtCore.Slot()
    def worker_finished(self, success):
        msg = "Finished "
        msg += "successfully!" if success else "with errors!" 
        self.label.setText(msg)

    def clicked(self):
    if hasattr(self, "worker") and self.worker:
        self.worker.stop()
        self.worker.quit()
        self.worker.wait()
        self.worker.deleteLater()
    print("Main:" + str(QtCore.QThread.currentThread()))
    #self.worker = Worker() # This works
        self.worker = SubWorker() # This locks up main thread
    self.worker._started.connect (self.worker_started)
        self.worker.progress.connect(self.worker_progress)
        self.worker._finished.connect(self.worker_finished)
    self.worker.start()

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

    main_window = MainWindow()
    main_window.show()

    app.exec_()
    sys.exit()