Constantly print Subprocess output while process is running

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:

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__

I would an approach like this where you can poll for the new line vs waiting for the entire subprocess to complete

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.

1 Like