Search code examples
pythoneventspyqt5initialization

How to display progress bar while initializing large data during class init


I'm using PyQt5. During MainWindow.__init__ in the code below, I want to initialize some large data (see MainWindow.initData) that takes a long time (20 seconds for example). During that time, I would like to display a progress bar. The progress bar cannot be displayed until after app.exec_() starts executing, thus I cannot call initData from within MainWindow.__init__. So I'm trying to execute initData by posting an event (postEvent) that will cause initData to execute while app.exec_ is executing. I looked at some examples in stackoverflow and arrived at the following code. The event does get posted and initData is called, but the progress bar is not displayed until after initData is completed.

Please advise on how to revise the code so that the progress bar will be displayed while initData is executing. Thanks.

import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QEvent

class MyEvent(QEvent):
    idType = QEvent.registerEventType()

    def __init__(self, data):
        QEvent.__init__(self, MyEvent.idType)
        self.data = data
        print("MyEvent.idType ", MyEvent.idType)

    def get_data(self):
        return self.data

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setWindowTitle("My MainWindow")
        self.qProgressBar = QtWidgets.QProgressBar(self)
        self.qProgressBar.setGeometry(100, 100, 200, 30)

        self.label = QtWidgets.QLabel("Hello", self)
        self.label.setGeometry(50, 50, 200, 20)

        tempEvent = MyEvent("12345")
        QtWidgets.QApplication.postEvent(self, tempEvent)

    def initData(self):
        # To demonstrate, do something that takes a long time (for example
        # 20 seconds) and update progress bar.
        print("begin initData")
        loop = 10000
        for i in range(loop):
            self.qProgressBar.setValue(int((i+1)/loop*100))
            for j in range(loop):
                temp = i + j

    def customEvent(self, event):
        print("customEvent:", event.type())
        if (event.type() == MyEvent.idType):
            self.label.setText("Received : {0}".format(event.get_data()))
            self.initData()
    
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    w = MainWindow()
    screen = QtWidgets.QDesktopWidget().screenGeometry()
    w.setGeometry(screen.width()//2-200, screen.height()//2-200, 400, 400) # x, y, Width, Height
    w.show()
    
    sys.exit(app.exec_())

Solution

  • Thanks musicamante. Here is the final result that works!

    import sys
    from PyQt5 import QtWidgets
    from PyQt5.QtCore import QObject, QThread, pyqtSignal
    from time import sleep
    
    class Worker_InitData(QObject):
        finished = pyqtSignal()
        progress = pyqtSignal(int)
    
        def run(self):
            """Long-running task."""
            for i in range(10):
                sleep(1)
                self.progress.emit(i)
            self.finished.emit()
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.setWindowTitle("My MainWindow")
            self.qProgressBar = QtWidgets.QProgressBar(self)
            self.qProgressBar.setGeometry(100, 100, 200, 30)
    
            self.label = QtWidgets.QLabel("Hello", self)
            self.label.setGeometry(50, 50, 200, 20)
    
            self.initData()
    
        def reportProgress(self, i):
            self.qProgressBar.setValue(int((i+1)/10*100))
        
        def initData(self):
            # Step 2: Create a QThread object
            self.thread = QThread()
            # Step 3: Create a worker object
            self.worker = Worker_InitData()
            # Step 4: Move worker to the thread
            self.worker.moveToThread(self.thread)
            # Step 5: Connect signals and slots
            self.thread.started.connect(self.worker.run)
            self.worker.finished.connect(self.thread.quit)
            self.worker.finished.connect(self.worker.deleteLater)
            self.thread.finished.connect(self.thread.deleteLater)
            self.worker.progress.connect(self.reportProgress)
            # Step 6: Start the thread
            self.thread.start()
       
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
    
        w = MainWindow()
        screen = QtWidgets.QDesktopWidget().screenGeometry()
        w.setGeometry(screen.width()//2-200, screen.height()//2-200, 400, 400) # x, y, Width, Height
        w.show()
        
        sys.exit(app.exec_())