wxPopen.py
changeset 110 a05e8b30c024
parent 89 0ab2868c6aa6
child 111 e2e498333fbc
equal deleted inserted replaced
109:f27ca37b6e7a 110:a05e8b30c024
    20 #
    20 #
    21 #You should have received a copy of the GNU General Public
    21 #You should have received a copy of the GNU General Public
    22 #License along with this library; if not, write to the Free Software
    22 #License along with this library; if not, write to the Free Software
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 #
       
    26 # based on wxPopen.py from boa-constructor
       
    27 #
       
    28 
    25 
    29 import time
    26 import time
    30 from StringIO import StringIO
    27 import wx
       
    28 import subprocess, ctypes
       
    29 import threading
       
    30 import os
    31 
    31 
    32 import wx
    32     
       
    33 class outputThread(threading.Thread):
       
    34     """
       
    35     Thread is used to print the output of a command to the stdout
       
    36     """
       
    37     def __init__(self, Proc, fd, callback=None, endcallback=None):
       
    38         threading.Thread.__init__(self)
       
    39         self.killed = False
       
    40         self.finished = False
       
    41         self.retval = None
       
    42         self.Proc = Proc
       
    43         self.callback = callback
       
    44         self.endcallback = endcallback
       
    45         self.fd = fd
    33 
    46 
    34 class ProcessRunnerMix:
    47     def run(self):
       
    48         outeof = False
       
    49         self.retval = self.Proc.poll()
       
    50         while not self.retval and not self.killed and not outeof:   
       
    51             outchunk = self.fd.readline()
       
    52             if outchunk == '': outeof = True
       
    53             if self.callback :
       
    54                 wx.CallAfter(self.callback,outchunk)
       
    55             self.retval=self.Proc.poll()
       
    56         if self.endcallback:
       
    57             err = self.Proc.wait()
       
    58             self.finished = True
       
    59             wx.CallAfter(self.endcallback, self.Proc.pid, self.retval)
    35 
    60 
    36     if wx.VERSION < (2, 6, 0):
    61 class ProcessLogger:
    37         def Bind(self, event, function, id = None):
    62     def __init__(self, logger, Command, finish_callback=None, no_stdout=False, no_stderr=False):
    38             if id is not None:
    63         self.logger = logger
    39                 event(self, id, function)
    64         self.Command = Command
    40             else:
    65         self.finish_callback = finish_callback
    41                 event(self, function)
    66         self.no_stdout = no_stdout
    42     
    67         self.no_stderr = no_stderr
    43     def __init__(self, input, handler=None):
    68         self.errlen = 0
    44         if handler is None:
    69         self.outlen = 0
    45             handler = self
    70         self.exitcode = None
    46         self.handler = handler
    71         self.outdata = ""
    47         handler.Bind(wx.EVT_IDLE, self.OnIdle)
    72         self.errdata = ""
    48         handler.Bind(wx.EVT_END_PROCESS, self.OnProcessEnded)
    73         self.finished = False
    49 
    74 
    50         input.reverse() # so we can pop
    75         self.Proc = subprocess.Popen(self.Command, 
    51         self.input = input
    76                                    cwd = os.getcwd(),
    52         
    77                                    stdin = subprocess.PIPE, 
    53         self.reset()
    78                                    stdout = subprocess.PIPE, 
       
    79                                    stderr = subprocess.STDOUT)
       
    80 #                                   stderr = subprocess.PIPE)
    54 
    81 
    55     def reset(self):
    82         self.outt = outputThread(
    56         self.process = None
    83                       self.Proc,
    57         self.pid = -1
    84                       self.Proc.stdout,
    58         self.output = []
    85                       self.output,
    59         self.errors = []
    86                       self.finish)
    60         self.inputStream = None
       
    61         self.errorStream = None
       
    62         self.outputStream = None
       
    63         self.outputFunc = None
       
    64         self.errorsFunc = None
       
    65         self.finishedFunc = None
       
    66         self.finished = False
       
    67         self.responded = False
       
    68 
    87 
    69     def execute(self, cmd):
    88         self.outt.start()
    70         self.process = wx.Process(self.handler)
       
    71         self.process.Redirect()
       
    72 
    89 
    73         self.pid = wx.Execute(cmd, wx.EXEC_ASYNC, self.process)
    90 #        self.errt = outputThread(
       
    91 #                      self.Proc,
       
    92 #                      self.Proc.stderr,
       
    93 #                      self.errors)
       
    94 #
       
    95 #        self.errt.start()
    74 
    96 
    75         self.inputStream = self.process.GetOutputStream()
    97     def output(self,v):
    76         self.errorStream = self.process.GetErrorStream()
    98         self.outdata += v
    77         self.outputStream = self.process.GetInputStream()
    99         self.outlen += 1
       
   100         if not self.no_stdout:
       
   101             self.logger.write(v)
    78 
   102 
    79         #self.OnIdle()
   103     def errors(self,v):
    80         wx.WakeUpIdle()
   104         self.errdata += v
    81     
   105         self.errlen += 1
    82     def setCallbacks(self, output, errors, finished):
   106         if not self.no_stderr:
    83         self.outputFunc = output
   107             self.logger.write_warning(v)
    84         self.errorsFunc = errors
       
    85         self.finishedFunc = finished
       
    86 
   108 
    87     def detach(self):
   109     def finish(self, pid,ecode):
    88         if self.process is not None:
   110         self.finished = True
    89             self.process.CloseOutput()
   111         self.exitcode = ecode
    90             self.process.Detach()
   112         if self.exitcode != 0:
    91             self.process = None
   113             self.logger.write(self.Command + "\n")
       
   114             self.logger.write_warning("exited with status %s (pid %s)\n"%(str(ecode),str(pid)))
       
   115         if self.finish_callback is not None:
       
   116             self.finish_callback(self,ecode,pid)
    92 
   117 
    93     def kill(self):
   118     def kill(self):
    94         if self.process is not None:
   119         self.outt.killed = True
    95             self.process.CloseOutput()
   120 #        self.errt.killed = True
    96             if wx.Process.Kill(self.pid, wx.SIGTERM) != wx.KILL_OK:
   121         if wx.Platform == '__WXMSW__':
    97                 wx.Process.Kill(self.pid, wx.SIGKILL)
   122             PROCESS_TERMINATE = 1
    98             self.process = None
   123             handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid)
       
   124             ctypes.windll.kernel32.TerminateProcess(handle, -1)
       
   125             ctypes.windll.kernel32.CloseHandle(handle)
       
   126         else:
       
   127             os.kill(self.Proc.pid)
    99 
   128 
   100     def updateStream(self, stream, data):
   129     def spin(self, timeout=None, out_limit=None, err_limit=None, keyword = None, kill_it = True):
   101         if stream and stream.CanRead():
   130         count = 0
   102             if not self.responded:
   131         while not self.finished:
   103                 self.responded = True
   132             if err_limit and self.errlen > err_limit:
   104             text = stream.read()
   133                 break
   105             data.append(text)
   134             if out_limit and self.outlen > out_limit:
   106             return text
   135                 break
   107         else:
   136             if timeout:
   108             return None
   137                 if count > timeout:
   109 
   138                     break
   110     def updateInpStream(self, stream, input):
   139                 count += 1
   111         if stream and input:
   140             if keyword and self.outdata.find(keyword)!=-1:
   112             line = input.pop()
   141                     break
   113             stream.write(line)
       
   114 
       
   115     def updateErrStream(self, stream, data):
       
   116         return self.updateStream(stream, data)
       
   117 
       
   118     def updateOutStream(self, stream, data):
       
   119         return self.updateStream(stream, data)
       
   120 
       
   121     def OnIdle(self, event=None):
       
   122         if self.process is not None:
       
   123             self.updateInpStream(self.inputStream, self.input)
       
   124             e = self.updateErrStream(self.errorStream, self.errors)
       
   125             if e is not None and self.errorsFunc is not None:
       
   126                 wx.CallAfter(self.errorsFunc, e)
       
   127             o = self.updateOutStream(self.outputStream, self.output)
       
   128             if o is not None and self.outputFunc is not None:
       
   129                 wx.CallAfter(self.outputFunc, o)
       
   130 
       
   131             #wx.WakeUpIdle()
       
   132             #time.sleep(0.001)
       
   133 
       
   134     def OnProcessEnded(self, event):
       
   135         self.OnIdle()
       
   136         pid,exitcode = event.GetPid(), event.GetExitCode()
       
   137         if self.process:
       
   138             self.process.Destroy()
       
   139             self.process = None
       
   140 
       
   141         self.finished = True
       
   142         
       
   143         # XXX doesn't work ???
       
   144         #self.handler.Disconnect(-1, wx.EVT_IDLE)
       
   145         
       
   146         if self.finishedFunc:
       
   147             wx.CallAfter(self.finishedFunc, pid, exitcode)
       
   148 
       
   149 class ProcessRunner(wx.EvtHandler, ProcessRunnerMix):
       
   150     def __init__(self, input):
       
   151         wx.EvtHandler.__init__(self)
       
   152         ProcessRunnerMix.__init__(self, input)
       
   153 
       
   154 def wxPopen3(cmd, input, output, errors, finish, handler=None):
       
   155     p = ProcessRunnerMix(input, handler)
       
   156     p.setCallbacks(output, errors, finish)
       
   157     p.execute(cmd)
       
   158     return p
       
   159 
       
   160 def _test():
       
   161     app = wx.PySimpleApp()
       
   162     f = wx.Frame(None, -1, 'asd')#, style=0)
       
   163     f.Show()
       
   164 
       
   165     def output(v):
       
   166         print 'OUTPUT:', v
       
   167     def errors(v):
       
   168         print 'ERRORS:', v
       
   169     def fin():
       
   170         p.Close()
       
   171         f.Close()
       
   172         print 'FINISHED'
       
   173 
       
   174 
       
   175     def spin(p):
       
   176         while not p.finished:
       
   177             wx.Yield()
   142             wx.Yield()
   178             time.sleep(0.01)
   143             time.sleep(0.01)
   179 
   144 
   180     def evt(self, event):
   145         if not self.outt.finished and kill_it:
   181         input = []
   146             self.kill()
   182         p = wxPopen3('''c:\\python23\\python.exe -c "print '*'*5000"''',
       
   183                  input, output, errors, fin, f)
       
   184         print p.pid
       
   185 
   147 
   186     app.MainLoop()
   148         return [self.exitcode, self.outdata, self.errdata]
   187 
   149 
   188 if __name__ == '__main__':
       
   189     _test()