#----------------------------------------------------------------------------- # Name: ProcessProgressDlg.py # Purpose: Dialog that shows errors and output of a process executed with # wxProcess. Operation can be canceled. # # Author: Riaan Booysen # # Created: 2001/02/04 # RCS-ID: $Id: ProcessProgressDlg.py,v 1.16 2005/05/06 14:00:05 riaan Exp $ # Copyright: (c) 2001 - 2005 Riaan Booysen # Licence: GPL #----------------------------------------------------------------------------- #Boa:Dialog:ProcessProgressDlg import wx from wx.lib.anchors import LayoutAnchors import os, time, sys # XXX Change to be non-modal, minimizable and run CVS operation in thread !! import Preferences from wxPopen import ProcessRunnerMix [wxID_PROCESSPROGRESSDLG, wxID_PROCESSPROGRESSDLGCANCELBTN, wxID_PROCESSPROGRESSDLGCMDSTXT, wxID_PROCESSPROGRESSDLGERRORTCTRL, wxID_PROCESSPROGRESSDLGKILLBTN, wxID_PROCESSPROGRESSDLGOUTPUTTCTRL, wxID_PROCESSPROGRESSDLGSPLITTERWINDOW, wxID_PROCESSPROGRESSDLGSTATUSGGE, wxID_PROCESSPROGRESSDLGSTATUSSTXT, ] = [wx.NewId() for _init_ctrls in range(9)] class ProcessProgressDlg(wx.Dialog, ProcessRunnerMix): def _init_ctrls(self, prnt): # generated method, don't edit wx.Dialog.__init__(self, id=wxID_PROCESSPROGRESSDLG, name='ProcessProgressDlg', parent=prnt, pos=wx.Point(313, 215), size=wx.Size(428, 363), style=wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE, title=self.dlg_caption) self.SetAutoLayout(True) self.SetClientSize(wx.Size(420, 336)) self.Bind(wx.EVT_CLOSE, self.OnProcessprogressdlgClose) self.cancelBtn = wx.Button(id=wxID_PROCESSPROGRESSDLGCANCELBTN, label='Cancel', name='cancelBtn', parent=self, pos=wx.Point(332, 304), size=wx.Size(80, 24), style=0) self.cancelBtn.SetConstraints(LayoutAnchors(self.cancelBtn, False, False, True, True)) self.cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancelbtnButton, id=wxID_PROCESSPROGRESSDLGCANCELBTN) self.cmdStxt = wx.StaticText(id=wxID_PROCESSPROGRESSDLGCMDSTXT, label='staticText1', name='cmdStxt', parent=self, pos=wx.Point(8, 8), size=wx.Size(404, 64), style=wx.ST_NO_AUTORESIZE) self.cmdStxt.SetConstraints(LayoutAnchors(self.cmdStxt, True, True, True, False)) self.splitterWindow = wx.SplitterWindow(id=wxID_PROCESSPROGRESSDLGSPLITTERWINDOW, name='splitterWindow', parent=self, point=wx.Point(8, 80), size=wx.Size(360, 192), style=self.splitterStyle) self.splitterWindow.SetConstraints(LayoutAnchors(self.splitterWindow, True, True, True, True)) self.errorTctrl = wx.TextCtrl(id=wxID_PROCESSPROGRESSDLGERRORTCTRL, name='errorTctrl', parent=self.splitterWindow, pos=wx.Point(0, 0), size=wx.Size(360, 80), style=wx.TE_MULTILINE | wx.TE_RICH, value='') self.errorTctrl.SetForegroundColour(wx.Colour(128, 0, 0)) self.outputTctrl = wx.TextCtrl(id=wxID_PROCESSPROGRESSDLGOUTPUTTCTRL, name='outputTctrl', parent=self.splitterWindow, pos=wx.Point(0, 87), size=wx.Size(360, 105), style=wx.TE_MULTILINE | wx.TE_RICH, value='') self.splitterWindow.SplitHorizontally(self.errorTctrl, self.outputTctrl, 80) self.statusStxt = wx.StaticText(id=wxID_PROCESSPROGRESSDLGSTATUSSTXT, label='staticText1', name='statusStxt', parent=self, pos=wx.Point(8, 288), size=wx.Size(292, 16), style=0) self.statusStxt.SetConstraints(LayoutAnchors(self.statusStxt, True, False, True, True)) self.statusGge = wx.Gauge(id=wxID_PROCESSPROGRESSDLGSTATUSGGE, name='statusGge', parent=self, pos=wx.Point(8, 312), range=100, size=wx.Size(208, 16), style=wx.GA_HORIZONTAL) self.statusGge.SetConstraints(LayoutAnchors(self.statusGge, True, False, True, True)) self.killBtn = wx.Button(id=wxID_PROCESSPROGRESSDLGKILLBTN, label='Kill', name='killBtn', parent=self, pos=wx.Point(242, 304), size=wx.Size(81, 24), style=0) self.killBtn.SetConstraints(LayoutAnchors(self.killBtn, False, False, True, True)) self.killBtn.Enable(False) self.killBtn.Bind(wx.EVT_BUTTON, self.OnKillbtnButton, id=wxID_PROCESSPROGRESSDLGKILLBTN) def __init__(self, parent, command, caption, modally=True, linesep=os.linesep, autoClose=True, overrideDisplay=''): self.dlg_caption = 'Progress' self.dlg_caption = caption self.splitterStyle = Preferences.splitterStyle self._init_ctrls(parent) self.splitterWindow.SetMinimumPaneSize(20) ProcessRunnerMix.__init__(self, []) self.Center(wx.BOTH) self.modally = True self.linesep = linesep self.autoClose = autoClose if overrideDisplay: self.cmdStxt.SetLabel(overrideDisplay) else: self.cmdStxt.SetLabel(command) self.execute(command, modally) if not modally: while not self.finished: wx.Yield() def execute(self, cmd, modally = True): self.killBtn.Enable(True) self.cancelBtn.SetLabel('Cancel') self.statusStxt.SetLabel('Waiting for response...') self.responded = False self.modally = modally ProcessRunnerMix.execute(self, cmd) def updateStream(self, stream, data): resp = self.responded try: return ProcessRunnerMix.updateStream(self, stream, data) finally: if self.responded == (not resp): self.statusStxt.SetLabel('Receiving response...') def updateErrStream(self, stream, data): txt = ProcessRunnerMix.updateErrStream(self, stream, data) if txt is not None: self.errorTctrl.SetFocus() self.errorTctrl.AppendText(txt) def updateOutStream(self, stream, data): txt = ProcessRunnerMix.updateOutStream(self, stream, data) if txt is not None: self.outputTctrl.SetFocus() self.outputTctrl.AppendText(txt) def OnIdle(self, event=None): # step the gauge to indicate activity if not self.finished: v = self.statusGge.GetValue() if v >= 100: v = 0 else: v = v + 1 self.statusGge.SetValue(v) ProcessRunnerMix.OnIdle(self, event) def OnProcessEnded(self, event): ProcessRunnerMix.OnProcessEnded(self, event) self.statusStxt.SetLabel('Response received.') self.prepareResult() self.statusGge.SetValue(0) self.killBtn.Enable(False) self.cancelBtn.SetLabel('OK') if self.modally and self.autoClose: self.EndModal(wx.OK) def OnProcessprogressdlgClose(self, event): try: self.detach() finally: event.Skip() def prepareResult(self): self.output = ''.join(self.output).split(self.linesep)[:-1] for idx in range(len(self.output)): self.output[idx] = self.output[idx] + os.linesep self.errors = ''.join(self.errors).split(self.linesep)[:-1] for idx in range(len(self.errors)): self.errors[idx] = self.errors[idx] + os.linesep def OnCancelbtnButton(self, event): if not self.finished: self.detach() self.prepareResult() if self.modally: self.EndModal(wx.CANCEL) else: self.EndModal(wx.OK) def OnKillbtnButton(self, event): if not self.finished: self.prepareResult() self.kill() if self.modally: self.EndModal(wx.CANCEL) else: self.EndModal(wx.OK) if __name__ == '__main__': app = wx.PySimpleApp() #cmd = '''python.exe -c "for i in range(2049):print '*',"''' cmd = '''python.exe -c "print '*'*5000"''' #cmd = '''python.exe -c "import time; time.sleep(10)"''' if 1: modal = 1 dlg = ProcessProgressDlg(None, cmd, 'Test', modal, autoClose=False) if modal: dlg.ShowModal() #print dlg.errors, dlg.output dlg.Destroy() app.MainLoop() else: class TestProcessRunner(ProcessRunner): def updateErrStream(self, stream, data): txt = ProcessRunner.updateErrStream(self, stream, data) if txt is not None: print 'error: %s'%txt def updateOutStream(self, stream, data): txt = ProcessRunner.updateOutStream(self, stream, data) if txt is not None: print 'output: %s'%txt def OnProcessEnded(self, event): ProcessRunnerMix.OnProcessEnded(self, event) # Test doesn't terminate, don't know why :( app.ExitMainLoop() tpr = TestProcessRunner() #cmd = 'cvs -H status' tpr.execute(cmd) app.MainLoop()