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:
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
PyQt/PySide 1:1 Coaching with Martin Fitzpatrick — Save yourself time and frustration. Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.
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.
Create GUI Applications with Python & Qt6 by Martin Fitzpatrick — (PyQt6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!