相关文章推荐
狂野的圣诞树  ·  Excel ...·  1 年前    · 
好帅的冲锋衣  ·  Kafka - ...·  1 年前    · 
谦和的冰棍  ·  WPF ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

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 ?

probably all GUIs works this way - they don't redraw window at once after you change something but when you end your function - so GUIs have to redraw window only once for all changes and it uses less CPU and window doesn't flick. – furas Jan 2, 2020 at 22:11

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()
                @Xeborus Thanks for the edit, provide a minimal reproducible example. Probably the part that you don't show is generating the problem, that code is the one I usually use for this type of case.
– eyllanesc
                Jan 2, 2020 at 23:09
                I figured out what was wrong : my heavy task was interacting with my mainwindow. The solution is to NOT interact with qt, or to use QThread from PySide2. (See my answer)
– TBrsx
                Jan 3, 2020 at 10:23

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

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.