0

I already soved this but am not feeling good about the solution I came up with. Here is the Issue: I created a command line utility that works fine.

eg: my main.py file is


def Parser():
    description = textwrap.dedent(
        '''Main Command Line Tool.
     For GUI based tool use: `main-gui`''')
    parser = argparse.ArgumentParser(description=description, prog="main")
    parser.add_argument("-f", "--foo", type=str, required=True, help='foo help')
    parser.add_argument("-b", "--bar", type=str, help='bar help')
    ...

    # this will not be shown in help
    parser.add_argument("--GUI", action="store_true", help=argparse.SUPPRESS)
    return parser

def task(args):
    ...
    print("I am doing the task, Alright!") # this means print will call sys.stdout.write function
    if args.GUI:
       if hasattr(sys.stdout, "set_progress"): # double check. But this is always True if args.GUI is True
           sys.stdout.set_progress(value)
    ...
def main():
    parser = Parser()
    args = parser.parse_args()
    result = task(args)

if __name__ == "__main__":
    main()

Which I run as main --foo Hello --bar World Which prints certain text in the terminal/console/shell whatever. Basically sys.stdout

Now, I was asked to make GUI for the same so I used PySide6.

Here is my gui.py file:

import threading
from easydict import EasyDict
from PySide6.QtCore import ...
from PySide6.QtGui import ...
from PySide6.QtWidgets import ...

from main import main

class EmittingStream(QObject):
    textWritten = Signal(str)
    progress = Signal(int)
    def write(self, text):
        self.textWritten.emit(text)

    def set_progress(self, value):
        self.progress.emit(value)

    def flush(self):
        pass

class GUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.initArgs()
        self.link_actions()

    def initUI(self):
        ...
        self.ui.progress_bar.setValue(0)

    def initArgs(self):
        self.args = EasyDict()
        self.args.foo = None
        self.args.bar = None

    def link_actions(self):
        ...
        self.ui.start_button.clicked.connect(self.start)

    def setProgress(self, progress):
        self.ui.progress_bar.setValue(progress)

    def showOuput(self, s):
        self.ui.output_textedit.append(s)

    def clear_output_textedit(self):
        self.ui.output_textedit.clear()

    def get_args(self):
        # Use the Gui text fields to access the foo and bar argument
        # so self.arg.foo = Hello and so on for all arguments
        # the arguments are exactly the same as the ones used for argparse
 
    def start(self):
        self.get_args()
        command = ["main"]
        command.extend(["--foo", self.args.foo])
        command.extend(["--bar", self.args.bar])
 
        ## This specifically added in parser if the script is being called from GUI
        ## This doesn't show up when done main --help
        command.extend(["--GUI"])

        sys.argv = command
        stream = EmittingStream()
        sys.stdout = stream  # This is what I beleive is Not good, But this work
        stream.textWritten.connect(self.showOuput)
        stream.progress.connect(self.setProgress)

        analysis_thread = threading.Thread(target=main())
        analysis_thread.start()

    # Setting sys.stdout back to default
    def __del__(self):
        sys.stdout = sys.__stdout__

def gui():
    app = QApplication(sys.argv)
    window = GUI()
    window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    gui()

I was able to tap into the hood of print function where it call sys.stdout.write function which overwrote it in EmittingStream. So whenever print is invoked in main.py, it sends a signal to GUI and I update the text in the field.

This works fine so I didn't touch it anymore. However, the thing that bothers me is changing the sys variable to do this.

This is not a duplicate of Passing sys.stdout as an argument to a process, but did give me idea about altering sys.stdout

ABD
  • 41
  • 5
  • I would use the `logging` module for all output. Both GUI and CLI. Here you can see how to get the logging output to show in your GUI: https://stackoverflow.com/questions/54643280/how-can-i-use-a-qtextbrowser-instead-of-the-console-in-python – mahkitah Aug 20 '23 at 10:21
  • 2
    What makes you think that altering the `sys.stdout` would be bad? There's absolutely nothing wrong with it, it's very common, considered conventional and perfectly standard. – musicamante Aug 20 '23 at 10:35

0 Answers0