Constantly print Subprocess output while process is running

Heads up! You've already completed this tutorial.

Cody_Jackson | 2021-04-26 20:21:54 UTC | #1

I have to call a legacy Bash program and output the results to a Qt window. The problem is the subprocess the Bash command is executed in doesn't return each output line as they happen; the subprocess waits until the entire Bash command is finished, then it dumps everything to the window. Thus, if the Bash command takes a long time, the user may think the system is frozen.

I can get the subprocess to print each line via a print(output.readlin()) to a normal terminal. But I can't do it within Qt. I have tried a number of different examples and either they don't work or continue to dump the results at the end of the process.

I don't know if this a problem that an unknown Qt module might help me with or if it's just something with how Python deals w/ subprocess calls to Bash commands, like it's running a batch job and only returning results once the batch is done.

Here is the trouble code I'm working with:

python
self.console = OutputConsole()

out = subprocess.run(["bash", "-c", f"source  path_to_setup_file -r && cd parent_directory && build_project_command"], stderr=subprocess.STDOUT, stdout=subprocess.PIPE)

self.console.on_update_text(out.stdout.decode())


class OutputConsole(QWidget):
        """Collects output data (sys.stdout) from subprocess calls to ocpidev. Prints the output to the console view."""
        process: QTextEdit

    def __init__(self, process: QTextEdit) -> None:
        """Create the process that manages text editing on the console"""
        super().__init__()

        sys.stdout = Stream()
        self.process = process
        self.process.moveCursor(QTextCursor.Start)
        self.process.ensureCursorVisible()
        self.process.setLineWrapColumnOrWidth(1000)
        self.process.setLineWrapMode(QTextEdit.FixedPixelWidth)

    def on_update_text(self, text: str) -> None:
        """Add new text to the console as stdout gets more data"""
        cursor: QTextCursor = self.process.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(text)
        self.process.setTextCursor(cursor)
        self.process.ensureCursorVisible()

    def __del__(self) -> None:
        """Reset sys.stdout to normal functionality"""
        sys.stdout = sys.__stdout__

mike2750 | 2021-04-29 22:55:02 UTC | #2

I would an approach like this where you can poll for the new line vs waiting for the entire subprocess to complete https://stackoverflow.com/questions/12523044/how-can-i-tail-a-log-file-in-python

Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

i know your running a script and not tailing a log but the output results and intended results would be equivalent in what your looking to do.


Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Constantly print Subprocess output while process is running was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.