Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. How to display a loading animated gif while a code is executing in backend of my Python Qt5 UI?
Forum Updated to NodeBB v4.3 + New Features

How to display a loading animated gif while a code is executing in backend of my Python Qt5 UI?

Scheduled Pinned Locked Moved Unsolved Qt for Python
5 Posts 3 Posters 6.4k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    CentreFF
    wrote on last edited by
    #1

    Environment:

    Python 3.7

    Qt5

    Windows 10

    Problem:

    When I execute my code, it shows immediately the UI, then it supposes to make some other preparing stuff and display a loading gif while these initialization tasks are running. But it does work. Instead of showing the gif, the UI is blocked(froze) waiting for my preparing script to finish its job.

    My script has a button to Run my main script "StartMyApp" and show an animated gif while MyApp is running without freezing my UI. I use multithread for this purpose. It works perfectly. I used this tutorial : https://www.learnpyqt.com/courses/concurrent-execution/multithreading-pyqt-applications-qthreadpool/

    So I thought by cloning the same logic, I could display another loading gif at the init of my UI but it didn't work. I missed something. I don't understand because the "Run" button works perfectly by showing the gif and running the main code without freezing the UI whereas my "preparing" code is not showing the gif and freezing my UI until it finishes.

    Does anyone understand the source of this issue?

    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, 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)
            # === 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, '#ff8a00',
                                  "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_()
    

    Edit:

    I added the xml source of MyAppUI.ui made with Qt Designer in order to reproduce my example:

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

    SGaistS 1 Reply Last reply
    0
    • C CentreFF

      Environment:

      Python 3.7

      Qt5

      Windows 10

      Problem:

      When I execute my code, it shows immediately the UI, then it supposes to make some other preparing stuff and display a loading gif while these initialization tasks are running. But it does work. Instead of showing the gif, the UI is blocked(froze) waiting for my preparing script to finish its job.

      My script has a button to Run my main script "StartMyApp" and show an animated gif while MyApp is running without freezing my UI. I use multithread for this purpose. It works perfectly. I used this tutorial : https://www.learnpyqt.com/courses/concurrent-execution/multithreading-pyqt-applications-qthreadpool/

      So I thought by cloning the same logic, I could display another loading gif at the init of my UI but it didn't work. I missed something. I don't understand because the "Run" button works perfectly by showing the gif and running the main code without freezing the UI whereas my "preparing" code is not showing the gif and freezing my UI until it finishes.

      Does anyone understand the source of this issue?

      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, 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)
              # === 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, '#ff8a00',
                                    "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_()
      

      Edit:

      I added the xml source of MyAppUI.ui made with Qt Designer in order to reproduce my example:

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

      SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi and welcome to devnet,

      @CentreFF said in How to display a loading animated gif while a code is executing in backend of my Python Qt5 UI?:

      fn)

          # Execute
          self.threadpool.start (worker)
      
          self.gif_loading.stop ()
          self.label_loading.setHidden (True)
      

      You are stopping and hiding the gif directly before then end of the constructor.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      C 1 Reply Last reply
      0
      • SGaistS SGaist

        Hi and welcome to devnet,

        @CentreFF said in How to display a loading animated gif while a code is executing in backend of my Python Qt5 UI?:

        fn)

            # Execute
            self.threadpool.start (worker)
        
            self.gif_loading.stop ()
            self.label_loading.setHidden (True)
        

        You are stopping and hiding the gif directly before then end of the constructor.

        C Offline
        C Offline
        CentreFF
        wrote on last edited by
        #3

        @SGaist Thank you so much for trying to help me.
        I move the location of these 2 lines:

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

        It didn't fix the issue.

        I even removed them. It didn't fix the issue.

        And I still have the problem of my UI froze while my preparing script is running. I don't understand because it runs like a new thread. And this technic worked for my "Run" button.

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          I do not know how fast your method is, but you usually do not start doing animation within the constructor of a widget. Until the constructor is done and the show event is called, there's no physical presence of the widget.

          AFAIK it is best to construct all your GUI pieces and once done trigger your long operation that should also show an animation. If you really want to do that from the constructor, use a single shot timer with a 0 delay that will then trigger as soon as possible once the event loop is running to call your method.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          0
          • T Offline
            T Offline
            temaprinter
            wrote on last edited by
            #5
            This post is deleted!
            1 Reply Last reply
            -1

            • Login

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved