PySide2/QT for Python : Widget不更新/GUI冻结

2 人关注

我正在使用pyside2和python。在一个Qmainwindow上,我创建了一个QpushButton和一个Qlabel。标签在mainWindow的初始阶段被隐藏,而pushButton被连接到以下函数,定义在mainWindow .NET中。

def buttonPushed(self):
        self.label.show()
        self.doStuff()
        self.label.hide()

doStuff() "函数需要15秒的执行时间,并按计划工作,所以在这段时间内标签应该是可见的,但它不是。如果我删除 "self.label.hide()",标签就会显示(当然再也不会隐藏)。

doStuff() "函数调用pyside2的函数。 我也试着在self.label.show()之后添加一个self.label.update(),但没有什么区别。

我的猜测是,这与QT如何安排任务有关:当我调用self.label.show()时,QT只在buttonPushed()结束后进行。

我应该怎么做?

1 个评论
可能所有的GUI都是这样工作的--在你改变某些东西后,他们不会立即重绘窗口,而是在你结束功能时重绘--所以GUI在所有的改变中只需重绘一次窗口,它使用的CPU更少,窗口也不会闪烁。
python
pyside2
TBrsx
TBrsx
发布于 2020-01-03
2 个回答
eyllanesc
eyllanesc
发布于 2020-01-03
已采纳
0 人赞同

如果一个任务执行了很长时间,它就会阻塞Qt的事件循环,导致某些事件不能正常工作,因为事件不能被传送,导致窗口冻结,产生你所说的效果。

所以在这些情况下,你必须在另一个线程中执行该任务(假设该任务不直接修改GUI),并通过信号将信息发送到GUI线程中。在你的案例中,必须在执行重任务之前发出一个信号,在它们分别连接到QLabel的show和hide方法之后再发出一个信号。

import threading
from PySide2.QtCore import Signal
class MainWindow(QMainWindow):
    started = Signal()
    finished = Signal()
    def __init__(self, parent=None):
        super().__init__(parent)
        # ...
        self.started.connect(self.label.show)
        self.finished.connect(self.label.hide)
        self.button.clicked.connect(self.buttonPushed)
    def buttonPushed(self):
        threading.Thread(target=self.doStuff, daemon=True).start()
    def doStuff(self):
        self.started.emit()
        # heavy task
        self.finished.emit()
    
@Xeborus 谢谢你的编辑,提供一个 最低限度的可重复的例子 .可能是你没有显示的部分产生了问题,那段代码是我通常用于这种情况的。
我发现了问题所在:我的重任是与我的主窗口进行交互。解决方案是不与qt交互,或者使用PySide2的QThread。
TBrsx
TBrsx
发布于 2020-01-03
0 人赞同

当线程中的任务不涉及QT时,@eyllanesc的解决方案是有效的。否则应该使用QThread和Qthreadpool。我使用了这个 教程 以了解如何做到这一点,但这里有一个小例子。

from PySide2.QtCore import  Signal, QRunnable, QThreadPool, QObject
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        # [...]
        self.threadpool = QThreadPool()
        self.button.clicked.connect(self.buttonPushed)
    def buttonPushed(self):
        builder = Builder()
        builder.signals.started.connect(self.label.show)
        builder.signals.finished.connect(self.label.hide)
        builder.signals.result.connect(self.doStuff)
        self.threadpool.start(builder)
    # a function that use the heavy task's results
    def doStuff(self, *args):
    # [...]
#defining the signals that our thread will use
class BuilderSignals(QObject):
    started = Signal()
    finished = Signal()
    result = Signal(*args)
#defining our thread
class Builder(QRunnable):
    def __init__(self,parent=None):
        super(Builder, self).__init__()
        self.signals = BuilderSignals()
    def run(self):
        self.signals.started.emit()