Using Python and Tkinter to capture script output

In order to build the assets for my games I have a series of python scripts that know how to take files TexturePacker, Tiled, and Blender and build them for use within the engine. My main platform is OS X and it has good terminal support and more importantly I am comfortable working with the terminal. The artist I work with is not so comfortable and he works on Windows where cmd.exe is painfully outdated and basically useless.

To counter this I wrote a small Python Tkinter script do display a window, text area, some checkboxes and a build button to make it a bit easier for him to work. Crucially it makes it easier to notice build errors and send me bug reports.

There is some tricky code in there to spawn a process and read it’s stdout and stderr in python.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
proc = subprocess.Popen(cmdlist,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.STDOUT,
                        universal_newlines=True)

while True:
    line = proc.stdout.readline()
    if not line:
        break
    self.addText(line)
    #this triggers an update of the text area, otherwise it doesn't update
    self.textarea.update_idletasks()

The key here is to spawn the process, redirect both it’s stdout to the pipe between this process and the child process. set it’s stderr to be the same as it’s stdout (so errors appear in the pipe) and then repeatedly read the stdout pipe one line at a time refreshing the textarea when a new line is added.

While this is going on the app is unfortunately unresponsive to the user (as Tk.mainloop() isn’t being called) but it’s still better than cmd.exe especially since the text widget has colored text and an infinite (as in 65KB) buffer.

Colorizing the text turned out to be incredibly easy with Tkinter. When adding a line to a text widget you pass tags to the widget. These tags represent traints that should be applied to the text.

Before using tags they need to be setup:

1
2
self.textarea.tag_config("errorstring", foreground="#CC0000")
self.textarea.tag_config("infostring", foreground="#008800")

Now calling

1
self.textarea.insert(INSERT, "ERROR", ('errorstring',))

inserts text with the errorstring tag resulting in red text appearing. Note that the tags parameter needs to be a tuple (thus the trailing comma after the tag name) and specifically NOT a list (which is a little annoying).

The code is available as a github gist here: It incudes a sample script that randomly prints messages, pauses and then returns after printing n messages.

Here are some screenshots so you can see what it is. They are all basically the same but showing it running on FreeBSD, OS X, Windows.

Running on FreeBSD Running on OSX Running on Windows XP

Hope this helps someone out there.

dazza