views:

427

answers:

2

I'm trying to subclass wx.Process such that I have a customized process launcher that fires events back to the main thread with data collected from the stdout stream. Is this a good way of doing things?

class BuildProcess(wx.Process):
    def __init__(self, cmd, notify=None):
       wx.Process.__init__(self, notify)
       print "Constructing a build process"
       self.Bind(wx.EVT_IDLE, self.on_idle)
       self.Redirect()
       self.cmd = cmd
       self.pid = None

   def start(self):
       print "Starting the process"
       self.pid = wx.Execute(self.cmd, wx.EXEC_ASYNC, self)
       print "Started."

   def on_idle(self, evt):
       print "doing the idle thing..."
       stream = self.GetInputStream()
       if stream.CanRead():
          text = stream.read()
          wx.PostEvent(self, BuildEvent(EVT_BUILD_UPDATE, self, data=text))
          print text

   def OnTerminate(self, *args, **kwargs):
       wx.Process.OnTerminate(self, *args, **kwargs)
       print "Terminating"

BuildEvent here is a custom subclass of wx.PyEvent. The process is starting, running, and terminating correctly, but my on_idle function is never executing, even though I'm sure I've bound it to the idle event.

A: 

From looking the the wxProcess docs, I don't think it works that way: wxProcess will create a new, seperate process running as a child of you current process. It is not possible to run methods connected to a message in such a process.

Maybe you can connect your idle event to a function or method in you main thread.

Or, mayby the wxThread class is what you really want to use.

Ber
A: 

The objective is not to call methods of another process, the objective is to redirect the stdout of another process back to the parent process via "update" events fired periodically as the process executes.

One solution is to use wx.Timer to periodically poll the output stream of the process, so that we don't rely on EVT_IDLE to do the work for us (I had trouble getting EVT_IDLE to fire)

class BuildProcess(wx.Process):

    def __init__(self, cmd, notify=None):
        wx.Process.__init__(self, notify)
        self.Redirect()
        self.cmd = cmd
        self.pid = None
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.on_timer)

    def start(self):
        wx.PostEvent(self, BuildEvent(EVT_BUILD_STARTED, self))
        self.pid = wx.Execute(self.cmd, wx.EXEC_ASYNC, self)
        self.timer.Start(100)

    def on_timer(self, evt):
        stream = self.GetInputStream()
        if stream.CanRead():
            text = stream.read()
            wx.PostEvent(self, BuildEvent(EVT_BUILD_UPDATE, self, data=text))


    def OnTerminate(self, *args, **kwargs):
        print "terminating..."
        stream = self.GetInputStream()
        if stream.CanRead():
            text = stream.read()
            wx.PostEvent(self, BuildEvent(EVT_BUILD_UPDATE, self, data=text))
        if self.timer:
            self.timer.Stop()
        wx.PostEvent(self, BuildEvent(EVT_BUILD_FINISHED, self))

By this method, every 100ms the output stream is read, packaged up, and shipped off as a build event.

Ryan