0

I have a really particular issue with a Python program I'm writing where I need a process to concurrently be running while a long-running function is carried out on the main thread

For clarity/example, let's say I have 2 necessary functions:

  1. A function that increments a progress bar by 1 every second
  2. A function that carries out a very long (30+ seconds) analysis and must be ran on the main thread (no exceptions, I've confirmed there is no workaround for this rule)

With that, I need these to concurrently run, so that the progress bar is incrementing while the analysis is being ran. So naturally, I make a thread to increment the progressbar and try to call that before calling the analysis function like so:

class ProgressThread(QThread):
    _signal = pyqtSignal(int)
    def __init__(self):
        super(Thread, self).__init__()
    def run(self):
        progress = True
        pct = 0
        while progress:     #while thread is running
            time.sleep(1)   #wait 1 sec
            pct += 1
            self._signal.emit(pct)  #emit signal to increment progressbar


class Window(QMainWindow):
    def __init__(self):
         self.progressbar = QProgressBar()  #init progressbar
    def main(self):
        self.thread = ProgressThread()
        self.thread._signal.connect(self.increment_progressbar)
        self.thread.start() #start progressbar incrementer thread

        result = long_analysis_function()   #start analysis function

    def increment_progressbar(self, percent):   #helper function to increment progressbar
        self.progressbar.setValue(percent)

However, this code blocks the ProgressThread until the long_analysis_function() finishes, so the progress bar just stays frozen during analysis and jumps to x% once the analysis function finishes.

I believe this could be solved by moving the analysis function to it's own thread, as the progress bar and analysis run in this scenario as epected, but then the analysis thread throws an error for not being in 'main thread', so I am literally unable to do that.

With that, how can I make it so my progress bar increment or thread properly while the analysis function runs concurrently in the main thread?

mmarion
  • 859
  • 8
  • 20
  • Re, "the progress bar...jumps to x%" that sounds like maybe the thread _is_ running, but the `self._signal.emit(pct)` is not having any effect on the GUI. Sounds like you are using QTWidgets for your GUI framework. I don't know QT (I don't know when/why/how often the `Window.main()` function will be called) but I'm guessing that the "main" thread that is running your `long_analysis_function()` also is the GUI thread. If that's true, then you should not expect to see anything happen on the screen until the `long_analysis_function()` returns. – Solomon Slow Oct 20 '21 at 18:08
  • 1
    It's impossible to answer this properly without seeing all the code in `long_analysis_function()`. Please therefore provide a [mcve]. – ekhumoro Oct 20 '21 at 18:42

1 Answers1

1

I faced a similar issue trying to implement a animated splash screen while Main Window was initializing. What I learned from that is, GUI classes can only be updated or manipulated from the main thread (GUI thread).

You have to do two task simultaneously.

  1. Updating progress bar (requires Main Thread)
  2. Do the long analysis (You insist it to be in Main Thread)

Logically, these two need to have in separate thread to achieve that. Since, both have to be in main thread, I guess it won't be possible, or, at least difficult to implement that.

And, Your progress thread is just incrementing percent every second, not reporting actual progress? Although, ProgressThread is emitting signal every second, but progressbar.setValue cannot actually update while main thread is busy.

One solution could be breaking up long analysis in small tasks, that can remember how much progress it made earlier and continue from that. Then, Run those small tasks in a loop. Every time a small task is finished, updated the progress bar. In this way, you can report actual progress as well. Or, Make arrangement to update the progress bar directly from long_analysis_function

But remember that it will make the whole GUI unresponsive while any task is running.

More Reading:

Rad
  • 113
  • 9