How to stream tqdm text content to PySide2 GUI?

36 Views Asked by At

I am using jMetalPy. It has a "ProgressBarObserver" that uses tqdm to show calculation progess

https://github.com/jMetal/jMetalPy/blob/main/jmetal/util/observer.py

I have a PySide2 based GUI that prints console content in a GUI, and to my surprise, everything but the tqdm content showed in the GUI

    class EmittingStream(QtCore.QObject):
        textWritten = Signal(str)

        def write(self, text):
            self.textWritten.emit(text)

        def flush(self):
            pass

.....

# Console print
    self.textBrowser = self.ui.textBrowser_log


    # Create a custom stream object for the QTextBrowser.
    self.textBrowserStream = EmittingStream()
    self.textBrowserStream.textWritten.connect(self.textBrowser.append)

    # Redirect output to the QTextBrowser, but not the output from pandas.
    sys.stdout = self.textBrowserStream
    pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.expand_frame_repr', False)

I am using PyCharm, the console prints in white but the tqdm content in red, clearly this text is different and probably needs to be handled differently. How do I get this to be included in my text stream output? For further clarity it prints fine in PyCharm, but the PySide2 GUI is only showing the normal white text print statements. Thank You

2

There are 2 best solutions below

2
Muhammad Zahid Faiz On

It seems like the issue you're encountering might be due to the difference in handling the tqdm output compared to regular print statements. tqdm uses terminal control codes to update the progress bar dynamically, which might not be properly interpreted when redirected to a QTextBrowser.

To handle tqdm output in your PySide2 GUI, you'll need to intercept and handle tqdm's output separately. One approach is to subclass the ProgressBarObserver class from jMetalPy and override its on_progress_bar_update method to emit signals with the tqdm progress updates. Then, you can connect these signals to your GUI for updating.

--- Add tqdm observer to the GUI ----

tqdm_observer = TqdmProgressBarObserver() self.textBrowser.add_tqdm_observer(tqdm_observer)

0
idroid8 On

The answer my Muhammad reads like a poor AI response.

I ended up modifying the jMetalpy ProgressBarObserver class, adding the print line was sufficient for me. It will just print the progress in a loop to the console/GUI

class ProgressBarObserver(Observer):

    def __init__(self, max: int) -> None:
        """ Show a smart progress meter with the number of evaluations and computing time.

        :param max: Number of expected iterations.
        """
        self.progress_bar = None
        self.progress = 0
        self._max = max

    def update(self, *args, **kwargs):
        if not self.progress_bar:
            self.progress_bar = tqdm(total=self._max, ascii=True, desc='Progress')

        evaluations = kwargs['EVALUATIONS']

        self.progress_bar.update(evaluations - self.progress)
        self.progress = evaluations
        
        ### I added this line here ###
        print(f"Evaluations: {evaluations}", file=sys.stdout)**

        if self.progress >= self._max:
            self.progress_bar.close()