0

I have a calculating function that need to be sent to Qrunnable Worker as a parameter. Then, the calculating function runs with a 0-100 loop calculating in Worker's run function. The Problem is: the function is a separate loop function, I want to update a QProgressBar in the window. But, in the Worker, a progress signal can't send back to update progress bar value before the whole calculating function completed. This is really weird to me. I tried following code by updating the QProgressBar in the loop of the calculating function. But calculating is OK, the QProgressBar stuck....Why? How to handle this kind of problem?

import sys
import time
import traceback

from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow,QProgressBar,QPushButton, QVBoxLayout, QWidget


class WorkerSignals(QObject):
    result_signal = pyqtSignal(str)


class Worker(QRunnable):
    def __init__(self, fn, *args, **kwargs):
        super().__init__()
        # Store constructor arguments (re-used for processing)
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

    @pyqtSlot()
    def run(self):
        result = self.fn()
        self.signals.result_signal.emit(result)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.btn = QPushButton('Start Test')
        self.btn.clicked.connect(self.control_progressBar)
        self.progressBar = QProgressBar()
        self.progressBar.setValue(0)
        self.vlayout = QVBoxLayout()
        self.vlayout.addWidget(self.btn)
        self.vlayout.addWidget((self.progressBar))
        self.widget = QWidget()
        self.widget.setLayout(self.vlayout)
        self.setCentralWidget(self.widget)
        self.thread = QThreadPool()
        self.result = 0
        self.show()

    def control_progressBar(self):
        worker = Worker(self.calculate)
        worker.signals.result_signal.connect(self.output)
        self.thread.start(worker)

    def calculate(self):
        for i in range(100):
            self.result += i*i*i
            self.progressBar.setValue(i)
        return str(self.result)

    def output(self, result):
        print(result)

app = QApplication(sys.argv)
window = MainWindow()
app.exec_()
NorthBig
  • 47
  • 1
  • 9
  • The function shouldn't be in the main class, and in that function you should emit the signal, not set the progress bar. With the code above is difficult to give you further help, as we would need to know exactly how it works. Is there a specific reason for having that function in the main class? – musicamante Jan 31 '22 at 13:53
  • The calculate function actually is a complicated calculating function that lasts about 1 minute. The reason why the function is in the main class is that the function will get some data before it runs. So, I have to send the whole as a parameter into the Qrunnable Worker. I know I can move the function into Worker of Qrunnable and send data as parameter. I got an idea now. I just use a QTimer in the main class and control progress bar. Is it OK? – NorthBig Jan 31 '22 at 14:13
  • 1
    If you need data to initialize the computation, then set it on the worker object before starting it. From there, you can emit the signal at regular intervals, and it will work properly as the signal will be emitted from the other thread. – musicamante Jan 31 '22 at 14:19

0 Answers0