0

I'm trying to learn how to update a progress bar in my main script when my for loop is running in an external script. It's my first time trying to use signals so I'm unsure if I've used them correctly. Here is what I've tried so far.

The first thing that I tried doing was creating a class "Signal" which would have the pyqtSignal that is keeping track of the progress bar. Then I created an a signal object and I have it emit a signal every time the for loop runs. Then in my main script I try connecting the signal object that I created to the setValue method of the progressbar. The progress bar updates till 5% and after that the window becomes unresponsive. I have two main questions regarding the code:

What is the reason for the window becoming unresponsive?

How to get the progress bar in the main script to update so that the window doesn't become unresponsive?

main.py

import sys
from external import *
from PyQt5.QtWidgets import QWidget, QApplication, QProgressBar, 
QPushButton, QVBoxLayout


class Example(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()


    def initUI(self):
        self.setWindowTitle('Testing')
        self.setGeometry(300, 300, 290, 150)

        self.vbox = QVBoxLayout()
        self.setLayout(self.vbox)

        self.progressbar = QProgressBar(self)
        self.button = QPushButton("run", self)

        self.vbox.addWidget(self.progressbar)
        self.vbox.addWidget(self.button)

        c.update.connect(self.progressbar.setValue)
        self.button.clicked.connect(test) 

        self.show()

if __name__ == '__main__':    
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

external.py

from PyQt5.QtCore import pyqtSignal, QObject
import time

class Signal(QObject):

    update = pyqtSignal(int) 


c = Signal()   

def test():
    for i in range(100):
        c.update.emit(i)
        time.sleep(1)
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Shah Kamal
  • 43
  • 1
  • 4

1 Answers1

0

Even if the for loop code is in another file it doesn't matter, as it's executed within the PyQt QApplication event loop (the one started with app.exec_()).
A time.sleep blocks everything until completed, including the GUI, that's why the interface becomes unresponsive.

You need to run the loop function in a separate thread.
This is a very basic solution:

import threading
[...]
    def initUI(self):
        [...]
        c.update.connect(self.updateProgressBar)
        self.button.clicked.connect(self.runTest) 

        self.show()

    def runTest(self):
        threading.Thread(target=test).start()
        self.button.setEnabled(False)

    def updateProgressBar(self, value):
        self.progressbar.setValue(value)
        if value == 100:
            self.button.setEnabled(True)

NB: I've set the range of the test function to 101, so that when it reaches 100 it enables the button again, since I've disabled it to avoid multiple and concurrent executions of the function.

That said, I wouldn't suggest you to follow this kind of approach, as it would certainly create issues in the future.
Have a look at this answer, which might let you understand a better (and suggested) approach with threads.

musicamante
  • 41,230
  • 6
  • 33
  • 58