在我的Python Qt5 UI后端执行代码时,如何显示加载的动画gif?

问题描述

环境:

Python 3.7

Qt5

Windows 10

问题:

当我执行我的代码时,它立即显示UI,然后假定在执行这些初始化任务时进行一些其他准备工作并显示加载gif。但这确实有效。 UI没有显示gif,而是被阻塞(冻结),等待我的准备脚本完成其工作。

我的脚本有一个按钮,可以运行我的主脚本“ StartMyApp”并在MyApp运行时显示动画gif,而不会冻结我的UI。我为此使用多线程。完美运作。我使用了本教程:https://www.learnpyqt.com/courses/concurrent-execution/multithreading-pyqt-applications-qthreadpool/

因此,我认为通过克隆相同的逻辑,我可以在UI的 init 中显示另一个加载gif,但它不起作用。我错过了什么。我不明白,因为“运行”按钮可以通过显示gif并运行主代码而不冻结UI来完美地工作,而我的“准备”代码则不显示gif并冻结我的UI,直到完成为止。

有人知道这个问题的根源吗?

from PyQt5 import QtWidgets,uic,QtGui
from PyQt5.QtCore import *
from PyQt5.QtGui import QMovie
import traceback,sys
class WorkerSignals(QObject):
    '''
    Defines the signals available from a running worker thread.

    Supported signals are:

    finished
        No data

    error
        `tuple` (exctype,value,traceback.format_exc() )

    result
        `object` data returned from processing,anything

    progress
        `int` indicating % progress

    '''
    finished = pyqtSignal ()
    error = pyqtSignal (tuple)
    result = pyqtSignal (object)
    progress = pyqtSignal (int)



class Worker (QRunnable):
    '''
    Worker thread

    Inherits from QRunnable to handler worker thread setup,signals and wrap-up.

    :param callback: The function callback to run on this worker thread. Supplied args and
                     kwargs will be passed through to the runner.
    :type callback: function
    :param args: Arguments to pass to the callback function
    :param kwargs: Keywords to pass to the callback function

    '''

    def __init__(self,fn,*args,**kwargs):
        super (Worker,self).__init__ ()

        # Store constructor arguments (re-used for processing)
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals ()

        # Add the callback to our kwargs
        self.kwargs['progress_callback'] = self.signals.progress

    @pyqtSlot ()
    def run(self):
        '''
        Initialise the runner function with passed args,kwargs.
        '''

        # Retrieve args/kwargs here; and fire processing using them
        try:
            result = self.fn (*self.args,**self.kwargs)
        except:
            traceback.print_exc ()
            exctype,value = sys.exc_info ()[:2]
            self.signals.error.emit((exctype,traceback.format_exc ()))
        else:
            self.signals.result.emit (result)  # Return the result of the processing
        finally:
            self.signals.finished.emit ()  # Done


class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        
        super(Ui,self).__init__()
        uic.loadUi('Ui/MyAppUI.Ui',self)
        # === We display the UI ==========
        self.show()
        # === THis will handle the MULTITHREAD PART ===================
        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())

        self.StartPreparingMyApp() #<======== This method doesn't work!!!!

        # === Associate methods to the buttons of the UI ==============        
        self.button_Report.clicked.connect (self.ButtonStartMyAppReport)        
        self.button_Run.clicked.connect (self.ButtonStartMyApp)
    
    def StartMyAppReport(self,progress_callback):
        #do some stuff

    def StartMyApp(self,progress_callback):
        # do some stuff

    def ButtonStartMyApp(self): #<=== This method works perfectly by showing the loading gif.
        # Pass the function to execute
        # === We need to block the Button Run and change its color
        self.button_Run.setEnabled (False)
        self.button_Run.setText ('Running...')
        self.button_Run.setStyleSheet ("background-color: #ffcc00;")
        self.label_logo.setHidden (True)
        self.label_running.setHidden (False)

        # === Play animated gif ================
        self.gif = QMovie ('ui/animated_gif_logo_UI_.gif')
        self.label_running.setMovie (self.gif)
        self.gif.start ()

        self.EditTextFieldUi (self.label_HeaderMsg1,'#ff8a00',"MyApp is running the tasks... You can press the button 'Report' to see what MyApp has done.")
        self.EditTextFieldUi (self.label_HeaderMsg2,"Press 'button 'Quit' to stop and turn off MyApp.")

        worker = Worker (self.StartMyApp)  # Any other args,kwargs are passed to the run function
        worker.signals.result.connect (self.print_output)
        worker.signals.finished.connect (self.thread_complete)
        worker.signals.progress.connect (self.progress_fn)

        # Execute
        self.threadpool.start (worker)

    def PreparingMyApp(self,progress_callback):
        #do some stuff
        return "Done"
    
    def ButtonStartMyAppReport(self):
        # Pass the function to execute
        worker = Worker (self.StartMyAppReport)  # Any other args,kwargs are passed to the run function
        worker.signals.result.connect (self.print_output)
        worker.signals.finished.connect (self.thread_complete)
        worker.signals.progress.connect (self.progress_fn)

        # Execute
        self.threadpool.start(worker)
        

    def StartPreparingMyApp(self): #<=== This method doesn't work !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        # === Play animated gif ================
        self.label_loading.setHidden (False)
        self.gif_loading = QMovie ('ui/loading.gif')
        self.label_loading.setMovie (self.gif_loading)
        self.gif_loading.start ()

        # Pass the function to execute
        worker = Worker (self.PreparingMyApp)  # Any other args,kwargs are passed to the run function
        worker.signals.result.connect (self.print_output)
        worker.signals.finished.connect (self.thread_complete)
        worker.signals.progress.connect (self.progress_fn)

        # Execute
        self.threadpool.start (worker)

        self.gif_loading.stop ()
        self.label_loading.setHidden (True)

        
if __name__ == '__main__':    
    app = QtWidgets.QApplication(sys.argv)
    window = Ui()
    app.exec_()

    

编辑:

我添加了用Qt Designer创建的MyAppUI.ui的xml源,以重现我的示例:

https://drive.google.com/file/d/1U9x0NmZ7GP6plzvRb6YgwIqaFHCz1PMc/view?usp=sharing

解决方法

一切都对您有用。 请注意,我已转移

self.gif_loading.stop()             # <---- +++
self.label_loading.setHidden(True)  # <---- +++
    

thread_complete方法中添加了QtCore.QThread.msleep (5000) 进入run方法以观察self.gif_loading

的过程
import sys
from PyQt5 import QtCore,QtWidgets,QtGui,uic
from PyQt5.Qt import *


class WorkerSignals(QObject):
    finished = pyqtSignal ()
    error = pyqtSignal (tuple)
    result = pyqtSignal (object)
    progress = pyqtSignal (int)


class Worker (QRunnable):
    def __init__(self,fn,*args,**kwargs):
        super (Worker,self).__init__ ()

        # Store constructor arguments (re-used for processing)
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals ()

        # Add the callback to our kwargs
        self.kwargs['progress_callback'] = self.signals.progress

    @pyqtSlot ()
    def run(self):
        '''
        Initialise the runner function with passed args,kwargs.
        '''

        # Retrieve args/kwargs here; and fire processing using them
        try:
            result = self.fn (*self.args,**self.kwargs)
            
            QtCore.QThread.msleep(5000)                        #   +++ !!!!!!!!!!!!!!!!!!!!!!
            
        except:
            traceback.print_exc ()
            exctype,value = sys.exc_info ()[:2]
            self.signals.error.emit((exctype,value,traceback.format_exc ()))
        else:
            self.signals.result.emit (result)  # Return the result of the processing
        finally:
            self.signals.finished.emit ()      # Done


class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        
        super(Ui,self).__init__()
        
#        uic.loadUi('Ui/MyAppUI.Ui',self)                                # !!!
        uic.loadUi('my_app_ui.ui',self)                                  # !!!
        
        # === We display the UI ==========
        self.show()
        # === THis will handle the MULTITHREAD PART ===================
        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())

        self.StartPreparingMyApp()                       # <======== This method  work !!!!

        # === Associate methods to the buttons of the UI ==============        
        self.button_Report.clicked.connect (self.ButtonStartMyAppReport)        
        self.button_Run.clicked.connect (self.ButtonStartMyApp)
    
    def StartMyAppReport(self,progress_callback):
        #do some stuff
        pass                                                             # +++

    def StartMyApp(self,progress_callback):
        # do some stuff
        pass                                                             # +++

    def ButtonStartMyApp(self): #<=== This method works perfectly by showing the loading gif.
        # Pass the function to execute
        # === We need to block the Button Run and change its color
        self.button_Run.setEnabled (False)
        self.button_Run.setText ('Running...')
        self.button_Run.setStyleSheet ("background-color: #ffcc00;")
        self.label_logo.setHidden (True)
        self.label_running.setHidden (False)

        # === Play animated gif ================
        self.gif = QMovie("D:/_Qt/__Qt/wait.gif")          # ('ui/animated_gif_logo_UI_.gif') !!!
        self.label_running.setMovie (self.gif)
        self.gif.start ()

#?        self.EditTextFieldUi (self.label_HeaderMsg1,'#ff8a00',#?                              "MyApp is running the tasks... You can press the button 'Report' to see what MyApp has done.")
#?        self.EditTextFieldUi (self.label_HeaderMsg2,#?                              "Press 'button 'Quit' to stop and turn off MyApp.")

        worker = Worker (self.StartMyApp)  # Any other args,kwargs are passed to the run function
        worker.signals.result.connect (self.print_output)
        worker.signals.finished.connect (self.thread_complete)
        worker.signals.progress.connect (self.progress_fn)

        # Execute
        self.threadpool.start (worker)

    def PreparingMyApp(self,progress_callback):
        #do some stuff
        return "Done"
    
    def ButtonStartMyAppReport(self):
        # Pass the function to execute
        worker = Worker (self.StartMyAppReport)  # Any other args,kwargs are passed to the run function
        worker.signals.result.connect (self.print_output)
        worker.signals.finished.connect (self.thread_complete)
        worker.signals.progress.connect (self.progress_fn)

        # Execute
        self.threadpool.start(worker)
        
    def StartPreparingMyApp(self): 
        print("!!! <=== This method work !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
        # === Play animated gif ================
        self.label_loading.setHidden (False)
        self.gif_loading = QMovie("D:/_Qt/__Qt/wait.gif")        # ('ui/loading.gif') !!!
        self.label_loading.setMovie (self.gif_loading)
        self.gif_loading.start ()

        # Pass the function to execute
        worker = Worker (self.PreparingMyApp)  # Any other args,kwargs are passed to the run function
        worker.signals.result.connect (self.print_output)
        worker.signals.finished.connect (self.thread_complete)
        worker.signals.progress.connect (self.progress_fn)

        # Execute
        self.threadpool.start (worker)

#        self.gif_loading.stop ()                                # ---
#        self.label_loading.setHidden (True)                     # ---
        
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv    
    def print_output(self,obj):
        print(f'def print_output(self,obj): {obj}')    
        
    def thread_complete(self,val='finished'):
        print(f'def thread_complete(self,obj): {val}') 
        self.gif_loading.stop ()                                  # <---- +++
        self.label_loading.setHidden (True)                       # <---- +++        
        
    def progress_fn(self,val):
        print(f'def progress_fn(self,obj): {val}')  
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        

        
if __name__ == '__main__':    
    app = QtWidgets.QApplication(sys.argv)
    window = Ui()
    app.exec_()

enter image description here

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...