I have an app that runs a list of long running commands and shows each
commands stdout in it's own TextCtrl. It mostly works, but I am not
happy with the implementation.
1. I hardly understand how it works, aka why it doesn't not work.
Like I don't see what is keeping objects from being destroyed - maybe
they are, but I have a stale pointer that happens to work, or maybe
something is keeping a reference count that I don't understand. I did
change my code around to something I think should work, but the fact
that my previous version worked and I don't know how isn't good. The
new version is still on the edge of my comprehension.
And trying to follow it when I am debugging still gives me a
headache. Mainly when the timer calls my .ShowIO().
Below is my code trimmed down to just what is needed to see what I am
working on. The full version is
https://github.com/CarlFK/dvsmon/blob/master/dvs-mon.py
foos is kinda redundant - it is basically the set of panels I add to
the empty frame. Isn't there a list of objects that have been added
to a container?
2. I am using a timer to poll for output every second. What I would
really like is to hear there is an event I can trap when the command
issues output. That seems more efficient, and likely much cleaner
code. Is my polling really the best/only way?
btw, I tried to use python's subprocess - there doesn't seem to be
something like stream.CanRead() and so p.stdin.read() would wait for
data to be there, which isn't good at all.
Example code: uses ping as the long running commands. ping is
actually more chatty - the real commands only output every few
minutes, but I need to see the output right away.
#!/usr/bin/python
import random
import wx
import wx.lib.sized_controls as sc
class CommandRunner(object):
ctr = 0
def __init__(self, cmd, parent):
self.cmd = cmd
self.panel = sc.SizedPanel(parent)
self.panel.SetSizerType('horizontal')
self.panel.SetSizerProps(expand=True)
self.txt_cmd = wx.TextCtrl(self.panel, value=self.cmd,
style=wx.TE_READONLY)
self.txt_cmd.SetSizerProps(proportion=40, expand=True)
self.panel2 = sc.SizedPanel(parent)
self.panel2.SetSizerType('vertical')
self.panel2.SetSizerProps(expand=True, proportion=2)
self.stdout = wx.TextCtrl( self.panel2, style=wx.TE_READONLY|
wx.TE_MULTILINE)
self.stdout.SetSizerProps(proportion=1, expand=True)
def RunCmd(self):
self.process = wx.Process(self.panel)
self.process.Redirect()
self.pid = wx.Execute(self.cmd, wx.EXEC_ASYNC, self.process)
print 'Executed: %s' % (self.cmd)
def ShowIO(self):
# flip color to show when it hit
c = [random.randrange(0,255) for i in range(4)]
self.txt_cmd.SetForegroundColour(c)
# move stdout to TextCtrl
stream = self.process.GetInputStream()
while stream.CanRead():
line = stream.read()
line = line.strip()
# print "O", line.__repr__()
self.stdout.AppendText("\n %d %s" % (self.ctr,line))
self.ctr += 1
def main():
app = wx.PySimpleApp()
# add command blocks:
frame = sc.SizedFrame(None, pos=(1,1), size=(500, 500))
panel = frame.GetContentsPane()
foos=[]
for i in range(3):
cmd = 'ping -i %s 127.0.0.1' % (i+1)
cr = CommandRunner(cmd, panel)
cr.RunCmd()
foos.append(cr)
frame.Show()
# setup poling
def OnTimer(evt):
for foo in foos:
foo.ShowIO()
panel.Bind(wx.EVT_TIMER, OnTimer)
timer = wx.Timer(panel)
timer.Start(500)
# wait in event loop:
app.MainLoop()
if __name__ == '__main__':
main()