简体   繁体   中英

PySide2/QT for Python : Widget not updating / GUI Freezing

I'm using pyside2 with python. On a Qmainwindow, I've created a QpushButton and a Qlabel. The label is hidden at the init of the mainWindow, and the pushButton is connected to the following function, defined in mainWindow :

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

The "doStuff()" function takes 15sec of exec time and works as intended so the label should be visible for this time, but it isn't. If I delete the "self.label.hide()", the label does show (and never hide anymore, of course).

The "doStuff()" function calls pyside2's functions. I also tried to add a self.label.update() just after the self.label.show(), but it makes no difference.

My guess is that is has something to do with how QT schedules tasks : when I call self.label.show(), QT only does it after buttonPushed() has ended.

What should I do ?

If a task is executed for a long time, it will block the Qt event loop causing certain ones to not work properly since the events cannot be transmitted causing the window to freeze, generating as an effect what you indicate.

So in those cases you must execute that task in another thread (assuming that task does not modify the GUI directly) and send the information to the GUI thread through signals. In your case, a signal must be emitted before executing the heavy task and another after they are connected to the show and hide method of the QLabel, respectively.

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()

@eyllanesc 's solution works when the task in the thread doesn't involve QT. One should use QThread and Qthreadpool otherwise. I used this tutorial to understand how to do it, but here's a small example :

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()
        #heavy task involving PySide2
        self.signals.result.emit(*args)
        self.signals.finished.emit()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM