Shadi
Shadi

Reputation: 143

Redirecting stdout to tkinter immediately (without waiting for the process to complete)

I am writing a python app to take some inputs from the user and call a shell script based on these inputs.

This shell script can run for quite sometime, and I want to redirect the output it produces (in realtime) to tkinter. I managed to do that, but it only happens after the shell script is completely finished, not as soon as the shell script "echos" something for example.

So main problem: 1. Output appears in the text widget only after shellScript.sh is exited (although if I run the same in the terminal manually, I see continuous output). 2. Side problem: all the "\n" printed in the output are just printed as "\n" and no new lines are added in the text widget.

here is what I have:

class RedirectText(object):
   def __init__(self, text_ctrl):
       """Constructor"""
       self.output = text_ctrl

   def write(self, string):
       self.output.insert(tk.END, string[2:])

class Gui(GuiApp):
   def __init__(self, master=None):
      redir = RedirectText(self.text_Output)
      sys.stdout = redir

  def testRun(self, event=None):
     p = subprocess.Popen(["./shellScript.sh"], stdout=subprocess.PIPE)
     print(p.communicate()[0])

Upvotes: 3

Views: 1239

Answers (1)

acw1668
acw1668

Reputation: 47219

As p.communicate() will wait for the process to complete, so use p.poll() and p.stdout.readline() instead. Also put the process in a thread to avoid blocking the main thread:

import threading
...
class Gui(GuiApp):
    ...
    def runScript(self):
        print('started')
        p = subprocess.Popen(['./shellScript.sh'], stdout=subprocess.PIPE, bufsize=1, text=True)
        while p.poll() is None: # check whether process is still running
            msg = p.stdout.readline().strip() # read a line from the process output
            if msg:
                print(msg)
        print('finished')

    def testRun(self, event=None):
        # create a thread to run the script
        threading.Thread(target=self.runScript, daemon=True).start()

Upvotes: 1

Related Questions