Capture output from stderr

Jeff Epler jepler at unpythonic.net
Tue Aug 5 22:15:57 EDT 2003


On Wed, Aug 06, 2003 at 12:30:13AM +0000, Grant Edwards wrote:
> I've always used a read handler for this in the past, and it worked fine.
> I don't have an example handy...

... this is found at _tkinter.createfilehandler() or
Tkinter.createfilehandler() [though this didn't work for me!], doesn't
exist on Windows (AFAIK), and seems to be undocumented in pydoc.  I think
they're a wrapper on Tcl_CreateFileHandler, which has a manpage.

Here's a little program I just put together, it shows the
output from a command in a scrolling text area, using popen and
_tkinter.createfilehandler.  It doesn't do anything in particular about
stderr, but that's a matter of switching to popen4 or a friend  (Another
way, on systems that use /bin/sh as their shell, is to use
'exec 2>&1; actual command' as the popen argument).  Other niceties need
to be observed, like making the Text and refuse keybooard
focus and be non-editable ...

On my system, a good command that takes some time to execute and only
produces a few hundred lines of output is
	$ python tktail.py rpm -qa
... but that'll only work on systems with rpm.  Something that would
work on more systems would be
	$ python tktail.py "for i in /*; do echo $i; sleep 1; done"

# The following code is placed in the public domain
import Tkinter, _tkinter, fcntl, os, sys

t = Tkinter.Tk()
tx = Tkinter.Text(wrap="char")
tx.pack(side="left", expand=1, fill="both")
s = Tkinter.Scrollbar(orient="v", command=tx.yview)
s.pack(side="left", fill="y")
tx.configure(yscrollcommand=s.set)

command = " ".join(sys.argv[1:])
t.wm_title("%s - tktail" % command)
p = os.popen(command, "r")
pf = p.fileno()

# Make reads from the popen'd command nonblocking
# (so read returns the bytes available without waiting)
fcntl.fcntl(pf, fcntl.F_SETFL, os.O_NONBLOCK)


def readfunc(fileobj, event_type):
	bytes = p.read()
	if bytes == '':
		bytes = "***END OF OUTPUT***"
		t.wm_title("%s - COMPLETED - tktail" % command)
		_tkinter.deletefilehandler(p)

	# bbox is a true value if the specified location is visible
	b = tx.bbox('end - 1l')

	tx.insert('end', bytes)

	# If the bottom line was visible before, scroll to the
	# new bottom.
	if b: tx.see('end')
	
_tkinter.createfilehandler(p, Tkinter.READABLE, readfunc)
t.mainloop()

Jeff





More information about the Python-list mailing list