PyQt5 - 即使在使用QThread

时间:2018-03-14 03:42:55

标签: pyqt pyqt5 qthread

我正在尝试在密集进程运行时在PyQt5 QMainWindow上显示加载gif。 QMovie暂停,而不是正常播放。据我所知,事件循环不应被阻止,因为密集进程在其自己的QObject中传递给它自己的QThread。相关代码如下:

的QMainWindow:

class EclipseQa(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)
        self.initUI()

    def initUI(self):
        ...
        self.loadingMovie = QMovie("./loading.gif")
        self.loadingMovie.setScaledSize(QSize(149, 43))
        self.statusLbl = QLabel(self)
        self.statusLbl.setMovie(self.loadingMovie)
        self.grid.addWidget(self.statusLbl, 6, 2, 2, 2, alignment=Qt.AlignCenter)
        self.statusLbl.hide()
        ...

    def startLoadingGif(self):
        self.statusLbl.show()
        self.loadingMovie.start()

    def stopLoadingGif(self):
        self.loadingMovie.stop()
        self.statusLbl.hide()

    def maskDose(self):
        self.startLoadingGif()

        # Set up thread and associated worker object
        self.thread = QThread()
        self.worker = DcmReadWorker()
        self.worker.moveToThread(self.thread)
        self.worker.finished.connect(self.thread.quit)
        self.worker.updateRd.connect(self.updateRd)
        self.worker.updateRs.connect(self.updateRs)
        self.worker.updateStructures.connect(self.updateStructures)
        self.worker.clearRd.connect(self.clearRd)
        self.worker.clearRs.connect(self.clearRs)
        self.thread.started.connect(lambda: self.worker.dcmRead(caption, fname[0]))
        self.thread.finished.connect(self.stopLoadingGif)

        self.maskThread.start()

    def showDoneDialog(self):
        ...
        self.stopLoadingGif()
        ...

工人阶级:

class DoseMaskWorker(QObject):
    clearRd = pyqtSignal()
    clearRs = pyqtSignal()
    finished = pyqtSignal()
    startLoadingGif = pyqtSignal()
    stopLoadingGif = pyqtSignal()
    updateMaskedRd = pyqtSignal(str)

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

    @pyqtSlot(name="maskDose")
    def maskDose(self, rd, rdName, rdId, rs, maskingStructure_dict):
        ...
        self.updateMaskedRd.emit(maskedRdName)
        self.finished.emit()

为简洁起见,' ...'表示我认为可能不相关的代码。

1 个答案:

答案 0 :(得分:1)

当发出线程lambda信号时,使用started来调用插槽可能会导致它在主线程中执行。您需要做几件事来解决这个问题。

首先,您对pyqtSlot的使用不包含maskDose方法的参数类型。您需要更新它以便它。据推测,您还需要对dcmRead方法执行此操作,您可以从lambda调用但未包含在代码中。有关详细信息,请参阅documentation

为了删除lambda的使用,你需要在EclipseQa类中定义一个新信号和一个新槽。应定义新信号,以便发出dcmRead方法所需的参数数量,并正确指定类型(此文档也在上面的链接中)。此信号应连接到workers dcmRead插槽(确保在将worker对象移动到线程后执行此操作,否则您可能会遇到this错误!)。插槽应该不带参数,并连接到线程started信号。插槽中的代码应该只使用适当的参数发出新信号,然后传递给dcmRead(例如self.my_new_signal.emit(param1, param2))。

注意:您可以通过从要检查的位置打印threading.current_thread().name来检查使用Python线程模块运行的任何代码(即使使用QThreads)。

注意2:如果您的线程是CPU绑定而不是IO绑定,您可能仍会遇到性能问题,因为Python GIL只允许单个线程在任何时间执行(它会在线程之间定期交换,但是两个线程中的代码都应该运行,可能不是你期望的性能。 QThreads(用C ++实现,理论上可以发布GIL)对此没有帮助,因为它们运行的​​是Python代码,因此GIL仍然存在。