|
#$Id: runProcess.py 277 2016-06-14 02:30:21Z sarkiss $
"""Runs commands using wx.Timer and subprocess module
"""
import wx
import subprocess, time, os, sys, errno, pipes
import wx.richtext as rt
PIPE = subprocess.PIPE
if subprocess.mswindows:
from win32file import ReadFile, WriteFile
from win32pipe import PeekNamedPipe
import msvcrt
else:
import select
import fcntl
class Popen(subprocess.Popen):
def recv(self, maxsize=None):
return self._recv('stdout', maxsize)
def recv_err(self, maxsize=None):
return self._recv('stderr', maxsize)
def send_recv(self, input='', maxsize=None):
return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
def get_conn_maxsize(self, which, maxsize):
if maxsize is None:
maxsize = 1024
elif maxsize < 1:
maxsize = 1
return getattr(self, which), maxsize
def _close(self, which):
getattr(self, which).close()
setattr(self, which, None)
if subprocess.mswindows:
def send(self, input):
if not self.stdin:
return None
try:
x = msvcrt.get_osfhandle(self.stdin.fileno())
(errCode, written) = WriteFile(x, input)
except ValueError:
return self._close('stdin')
except (subprocess.pywintypes.error, Exception), why:
if why[0] in (109, errno.ESHUTDOWN):
return self._close('stdin')
raise
return written
def _recv(self, which, maxsize):
conn, maxsize = self.get_conn_maxsize(which, maxsize)
if conn is None:
return None
try:
x = msvcrt.get_osfhandle(conn.fileno())
(read, nAvail, nMessage) = PeekNamedPipe(x, 0)
if maxsize < nAvail:
nAvail = maxsize
if nAvail > 0:
(errCode, read) = ReadFile(x, nAvail, None)
except ValueError:
return self._close(which)
except (subprocess.pywintypes.error, Exception), why:
if why[0] in (109, errno.ESHUTDOWN):
return self._close(which)
raise
if self.universal_newlines:
read = self._translate_newlines(read)
return read
else:
def send(self, input):
if not self.stdin:
return None
if not select.select([], [self.stdin], [], 0)[1]:
return 0
try:
written = os.write(self.stdin.fileno(), input)
except OSError, why:
if why[0] == errno.EPIPE: #broken pipe
return self._close('stdin')
raise
return written
def _recv(self, which, maxsize):
conn, maxsize = self.get_conn_maxsize(which, maxsize)
if conn is None:
return None
flags = fcntl.fcntl(conn, fcntl.F_GETFL)
if not conn.closed:
fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
try:
if not select.select([conn], [], [], 0)[0]:
return ''
r = conn.read(maxsize)
if not r:
return self._close(which)
if self.universal_newlines:
r = self._translate_newlines(r)
return r
finally:
if not conn.closed:
fcntl.fcntl(conn, fcntl.F_SETFL, flags)
class ProcessPanel(wx.Panel):
def __init__(self, parent, cmdTxtList, cwdTxt, outputFile, checkResults, use_stdout=False):
wx.Panel.__init__(self, parent, -1)
if sys.platform == "win32":
self.cmdTxt = subprocess.list2cmdline(cmdTxtList)
else:
if len(cmdTxtList) > 1:
self.cmdTxt = "'"+pipes.quote(cmdTxtList[0])+"' " + ' '.join([pipes.quote(x) for x in cmdTxtList[1:]] )
elif len(cmdTxtList) == 1:
self.cmdTxt = "'"+ pipes.quote(cmdTxtList[0])+"'"
self.cwdTxt = cwdTxt
self.outputFile = outputFile
self.checkResults = checkResults #function to call after thread is finished
self.outputTxt = ''
self.use_stdout = use_stdout
self.MakeLocalGUI()
self.worker = None
self.Bind(wx.EVT_TIMER, self.OnTestTimer)
self.scrollCounter = 0
def MakeLocalGUI(self):
# Make the controls
self.cmdTextCtrl = wx.TextCtrl(self, -1, self.cmdTxt)
self.cwdTextCtrl = wx.TextCtrl(self, -1, self.cwdTxt)
self.outTextCtrl = rt.RichTextCtrl(self, -1, '', style=rt.RE_READONLY)
try:
style = wx.richtext.TextAttrEx()
font = wx.Font(10, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
style.SetFont(font) #this can be set as a preference
self.outTextCtrl.SetBasicStyle(style)
except:
pass
if not self.use_stdout:
self.gauge = wx.Gauge(self, -1)
self.button = wx.Button(self, -1, 'Terminate')
self.button.Bind(wx.EVT_BUTTON, self.OnTerminate)
# Do the layout
#box1 = wx.BoxSizer(wx.HORIZONTAL)
cmdSizer = wx.FlexGridSizer(2, 2, 2, 2)
cmdSizer.Add(wx.StaticText(self, -1, 'Command line:'), 0, wx.LEFT|wx.RIGHT|wx.NORTH, 5)
cmdSizer.Add(self.cmdTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.NORTH, 5)
#box2 = wx.BoxSizer(wx.HORIZONTAL)
cmdSizer.Add(wx.StaticText(self, -1, 'Working directory:'), 0, wx.LEFT|wx.RIGHT, 5)
cmdSizer.Add(self.cwdTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
cmdSizer.AddGrowableCol(1)
bottomBox = wx.BoxSizer(wx.HORIZONTAL)
if not self.use_stdout:
bottomBox.Add(self.gauge, 1, wx.EXPAND|wx.ALL, 5)
bottomBox.Add(self.button, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(cmdSizer, 0, wx.EXPAND)
sizer.Add(self.outTextCtrl, 1, wx.EXPAND|wx.ALL, 5)
sizer.Add(bottomBox, 0, wx.ALIGN_RIGHT|wx.EXPAND)
self.SetSizer(sizer)
self.SetAutoLayout(True)
def Start(self):
"""Start Computation."""
# Trigger the worker thread unless it's already busy
if not self.worker:
# if os.path.exists(self.outputFile):
# self.TopLevelParent.documentsView.ClosePath(self.outputFile)
#os.rename(self.outputFile, self.outputFile+"~")
process = Popen(self.cmdTxt, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=self.cwdTxt, shell=True)
self.process = process
self.t1 = wx.Timer(self)
self.t1.Start(1000)
def ScrollToEnd(self):
self.scrollCounter += 1
if self.scrollCounter> 2:
self.outTextCtrl.ShowPosition(self.outTextCtrl.GetLastPosition())
self.scrollCounter = 0
def OnTestTimer(self, event):
returncode = self.process.poll()
if returncode or returncode == 0:
self.t1.Stop()
del self.t1
if not os.path.exists(self.outputFile):
stderrTxt = self.process.stderr.read()
if stderrTxt:
wx.LogError("Error running " + self.cmdTxt)
wx.LogError("Working Directory: " + self.cwdTxt)
wx.LogError("Stderr: " + stderrTxt)
wx.CallAfter(self.checkResults, page=self, success=False, outputFile=self.outputFile)
return
else:
if self.use_stdout:
txt = self.process.recv()
if txt:
self.outTextCtrl.AppendText(txt)
self.ScrollToEnd()
self.button.SetLabel("Close")
wx.CallAfter(self.checkResults, page=self, success=True, outputFile=self.outputFile)
if self.use_stdout:
txt = self.process.recv()
if txt:
self.outTextCtrl.AppendText(txt)
self.ScrollToEnd()
elif os.path.exists(self.outputFile):
txt = open(self.outputFile).read()
if txt:
if self.outputTxt:
outTxt = txt.split(self.outputTxt)
else:
outTxt = ['', txt]
if len(outTxt)>1:
if outTxt[1]:
self.outTextCtrl.AppendText(outTxt[1])
self.ScrollToEnd()
self.gauge.Pulse()
self.outputTxt = txt
def OnTerminate(self, event):
#self.worker.Abort()
if not self.use_stdout:
self.t1.Stop()
del self.t1
wx.CallAfter(self.checkResults, success="Terminated", outputFile=self.outputFile, page=self)
try:
if os.name != 'posix':
import ctypes
ctypes.windll.kernel32.TerminateProcess(int(self.process._handle), -1)
else:
import signal
os.kill(self.process.pid, signal.SIGTERM)
except:
pass
|