Search code examples
pythonmultithreadingpyqtpyqt5qthread

Why is my loading screen not showing up using QThread?


I'm making a desktop application in which once databases are being loaded I want to display a loading screen. A simple search led me to use gif files in QLabel with QThread object. But in my case QThread will not show anything. The thread works fine, but there is something wrong with my implementation, and I cannot figure out what. My sample code is as follows:

from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QDialog, QApplication, QPushButton
from PyQt5.QtCore import QThread
from PyQt5.QtGui import QMovie
import sys
import time


class myThread(QThread):
    def run(self):

        test = QWidget()   # Only creating this to give parent to QDialog and QLabel objects in upcoming lines
        dialog = QDialog(test)

        vbox = QVBoxLayout()
        lbl = QLabel(test)
        self.moviee = QMovie('Loading.gif')
        lbl.setMovie(self.moviee)
        self.moviee.start()
        vbox.addWidget(lbl)
        dialog.setLayout(vbox)
        dialog.show()

    def stop(self):
        self.moviee.stop()


class Main(QWidget):
    def __init__(self):
        super().__init__()

        print('Thread is to be called here...')
        thread = myThread()
        thread.run()

        print('Thread has been called...')

        btn= QPushButton('Test button')
        vbox = QVBoxLayout()
        vbox.addWidget(btn)
        self.setLayout(vbox)

        time.sleep(5)     # sleep function used to emulate funcitons in actual program

        # thread.stop()
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Main()
    sys.exit(app.exec_())

The block of code with QMovie object works fine when it is in the Main loop, so there is definitely somthing wrong with my implementation of QThread.


Solution

  • There are a couple of issues with your code. As @musicamante remarkes, you cannot create widget outside of the main thread. This means you can't create the dialog within myThread.run. Instead you could move the management of the dialog to Main and use slots and signals to open and close the dialog.

    Secondly, all time-consuming tasks like your time.sleep(5) should be put in myThread.run.

    With this in mind, you could do something like this

    from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QDialog, QApplication, QPushButton
    from PyQt5.QtCore import QThread
    from PyQt5.QtGui import QMovie
    import sys
    import time
    
    
    class myThread(QThread):
        def run(self):
            # time consuming actions
            time.sleep(5)
    
    
    class Main(QWidget):
    
        def __init__(self):
            super().__init__()
    
            print('Thread is to be called here...')
            self.load()
            print('Thread has been called...')
    
            btn= QPushButton('Test button')
            vbox = QVBoxLayout()
            vbox.addWidget(btn)
            self.setLayout(vbox)
            self.show()
    
        def load(self):
            # setup dialog
            dialog = QDialog(self)
            vbox = QVBoxLayout()
            lbl = QLabel(self)
            self.moviee = QMovie('Loading.gif')
            lbl.setMovie(self.moviee)
            self.moviee.start()
            vbox.addWidget(lbl)
            dialog.setLayout(vbox)
    
            # setup thread
            thread = myThread()
            thread.finished.connect(thread.deleteLater)
            thread.finished.connect(dialog.close)
            thread.finished.connect(dialog.deleteLater)
            thread.start()
    
            dialog.exec()
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = Main()
        app.exec()