0

I have the below command in my python script running a command line application which is currently running 'blind' in the background. If I run this command from a command line, mac or PC, I get a real-time readout of that application running, as it runs for 40 mins+ and reports various different info while it is running. Currently as I say it runs blind and gives me all the info at the end on a readout in python, but I want it to essentially open a command line and run so I can see all the info in real-time, but i can't find a way to do this. Here is my current code using a subprocess.Popen. Any other ways?

command1 = transporterLink + " -m verify -f " + indir1 + " -u " + username + " -p " + password + " -o " + indir1 + "\\VerifyLog.txt -s " + provider1 + " -v eXtreme"
        process = subprocess.Popen(command1, stdout=subprocess.PIPE, shell=True)
speedyrazor
  • 2,645
  • 5
  • 27
  • 47
  • related: [Python: read streaming input from subprocess.communicate()](http://stackoverflow.com/q/2715847/4279) – jfs Feb 18 '14 at 17:57

2 Answers2

1

I wrote about this a few years ago, but I don't think I titled the article very well:

Basically you need to redirect stdout. I usually do something like this:

class RedirectText:
    def __init__(self,aWxTextCtrl):
        self.out=aWxTextCtrl

    def write(self,string):
        self.out.WriteText(string)

And then in my actual wx class, I do something like this in the init:

self.redir=RedirectText(log)
sys.stdout=self.redir

Then when you call subprocess, you would do something like this example:

def pingIP(self, ip):
    proc = subprocess.Popen("ping %s" % ip, shell=True, 
                            stdout=subprocess.PIPE) 
    print
    while True:
        line = proc.stdout.readline()                        
        wx.Yield()
        if line.strip() == "":
            pass
        else:
            print line.strip()
        if not line: break
    proc.wait()

Note the wx.Yield() call. That allows wxPython to update when we print the line to stdout, which we have redirected to a text control.

Here is an example of sorts:

import subprocess
import sys
import wx

class RedirectText:
    def __init__(self,aWxTextCtrl):
        self.out=aWxTextCtrl

    def write(self,string):
        self.out.WriteText(string)

########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Test")
        panel = wx.Panel(self)
        log = wx.TextCtrl(panel, wx.ID_ANY, size=(300,100),
                          style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        btn = wx.Button(panel, label="Run")
        btn.Bind(wx.EVT_BUTTON, self.onRun)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(log, 1, wx.ALL|wx.EXPAND, 5)
        sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
        panel.SetSizer(sizer)

        self.redir=RedirectText(log)
        sys.stdout=self.redir

    #----------------------------------------------------------------------
    def onRun(self, event):
        """"""
        command1 = transporterLink + " -m verify -f " + indir1 + " -u " + username + " -p " + password + " -o " + indir1 + "\\VerifyLog.txt -s " + provider1 + " -v eXtreme"
        process = subprocess.Popen(command1, stdout=subprocess.PIPE, shell=True)
        while True:
            line = process.stdout.readline()
            wx.Yield()
            print line
            if not line:
                break
        process.wait()

#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    frame.Show()
    app.MainLoop()
Mike Driscoll
  • 31,394
  • 6
  • 39
  • 83
  • Thanks for you solution Mike, i'm a little new to python, could you elaborate on your solution to include how I would incorporate it into my code please, sorry to ask. – speedyrazor Feb 07 '14 at 14:46
  • I don't really know how you plan to call your command, so I just wrote a little wrapper around it that uses a button event to execute it. – Mike Driscoll Feb 07 '14 at 16:36
  • Mike, you are a star, cheers. I am getting AttributeError: 'Popen' object has no attribute 'readline'. It's complaining about line = process.readline(). Do I need to import another module? – speedyrazor Feb 07 '14 at 16:52
  • No, I just mis-typed. It should be line = process.stdout.readline(). I fixed the example too. – Mike Driscoll Feb 07 '14 at 16:59
  • Thanks for that. It now runs the command, but nothing appears in the window, I can see my application running and reporting in the background in the terminal window which is open via idle, but still nothing appears in the wx window. Any ideas please? – speedyrazor Feb 07 '14 at 21:34
  • Try doing the ping command I mentioned earlier. I know that worked. Maybe there's something odd that your app is doing that is making it harder to caption. – Mike Driscoll Feb 07 '14 at 22:32
  • Thanks Mike, if it is my application thats is going off to do it's thing, which takes about 40 mins, is there anyway to get that real-time feedback while the app is running? – speedyrazor Feb 08 '14 at 06:48
  • If it's logging something, you might be able to check that log every so often, read it and update the display. – Mike Driscoll Feb 10 '14 at 15:25
  • you could [use `iter(proc.stdout.readline, b'')` instead of the `while` loop](http://stackoverflow.com/a/17698359/4279). Also, don't use `shell=True` unnecessarily. – jfs Feb 18 '14 at 17:55
-1

I go it to work by changing this section from Mikes answer, but only trouble is now when the application ends and the output finishes printing lines my python freezes. Any ideas why?

def onRun(self, event):
    """"""
    command1 = transporterLink + " -m verify -f " + indir1 + " -u " + username + " -p " + password + " -o " + indir1 + "\\VerifyLog.txt -s "
+ provider1 + " -v eXtreme"
    master, slave = pty.openpty()
    process = Popen(command1, shell=True, stdin=PIPE, stdout=slave, stderr=slave, close_fds=True)
    stdout = os.fdopen(master)
    while True:
        line = stdout.readline()
        wx.Yield()
        print line.rstrip()
        if not line:
            break
    process.wait()
speedyrazor
  • 2,645
  • 5
  • 27
  • 47
  • here's an example on how to use [`pty` module to read output from a subprocess](http://stackoverflow.com/a/12471855/4279) – jfs Feb 18 '14 at 17:50