PyQt5 - 即使使用 QThread,MainWindow 中的 QMovie 也无法播放
Posted
技术标签:
【中文标题】PyQt5 - 即使使用 QThread,MainWindow 中的 QMovie 也无法播放【英文标题】:PyQt5 - QMovie in MainWindow doesn't play even when using QThread 【发布时间】:2018-03-14 03:42:54 【问题描述】:我试图在我的 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】:您在发出线程started
信号时使用lambda
调用插槽可能会导致它在主线程中执行。您需要做几件事来解决此问题。
首先,您对pyqtSlot
的使用不包含maskDose
方法的参数类型。您需要对其进行更新以使其正常运行。大概您还需要为您从 lambda
调用但未包含在您的代码中的 dcmRead
方法执行此操作。有关详细信息,请参阅documentation。
为了消除对 lambda 的使用,您需要在 EclipseQa
类中定义一个新信号和一个新槽。应该定义新信号,以便发出 dcmRead
方法所需数量的参数,并正确指定类型(相关文档也在上面的链接中)。这个信号应该连接到工作线程dcmRead
插槽(确保在工作线程对象被移动到线程之后执行此操作,否则您可能会遇到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 仍然存在。
【讨论】:
完美!感谢您的大力帮助:)以上是关于PyQt5 - 即使使用 QThread,MainWindow 中的 QMovie 也无法播放的主要内容,如果未能解决你的问题,请参考以下文章
PyQt5中多线程模块QThread解决界面卡顿无响应问题,线程池ThreadPoolExecutor解决多任务耗时操作问题
PyQt5中多线程模块QThread解决界面卡顿无响应问题,线程池ThreadPoolExecutor解决多任务耗时操作问题
PyQt5 - 使用QThread更新Qtablewidget