Redirecting stdout seems to be a pretty common request on the wxPython users group, so I decided to see how easy it would be to do it with Tkinter. The typical use case for redirecting stdout or stderr is that you are calling some other process (like ping or tracert) and you want to catch what it’s doing to put it into your UI. Usually you can just use Python’s subprocess module and call its communicate() method to access the data. Then you can just print it to stdout and it will magically appear in your UI’s widget of choice.
Our finished user interface will look something like the following:
Let’s find out how to do this with Tkinter:
import ScrolledText import sys import tkFileDialog import Tkinter ######################################################################## class RedirectText(object): """""" #---------------------------------------------------------------------- def __init__(self, text_ctrl): """Constructor""" self.output = text_ctrl #---------------------------------------------------------------------- def write(self, string): """""" self.output.insert(Tkinter.END, string) ######################################################################## class MyApp(object): """""" #---------------------------------------------------------------------- def __init__(self, parent): """Constructor""" self.root = parent self.root.title("Redirect") self.frame = Tkinter.Frame(parent) self.frame.pack() self.text = ScrolledText.ScrolledText(self.frame) self.text.pack() # redirect stdout redir = RedirectText(self.text) sys.stdout = redir btn = Tkinter.Button(self.frame, text="Open file", command=self.open_file) btn.pack() #---------------------------------------------------------------------- def open_file(self): """ Open a file, read it line-by-line and print out each line to the text control widget """ options = {} options['defaultextension'] = '.txt' options['filetypes'] = [('all files', '.*'), ('text files', '.txt')] options['initialdir'] = '/home' options['parent'] = self.root options['title'] = "Open a file" with tkFileDialog.askopenfile(mode='r', **options) as f_handle: for line in f_handle: print line #---------------------------------------------------------------------- if __name__ == "__main__": root = Tkinter.Tk() root.geometry("800x600") app = MyApp(root) root.mainloop()
That was a fair bit of code. Let’s take a minute or two to break it down. First off, we imported ScrolledText, which is text control that includes a scroll bar. We also imported tkFileDialog which gives us the ability to open a file. To make this example really simple, we will just use the dialog to open a text file and print it out line-by-line to stdout.
The first class we see is called RedirectText. It takes a text control widget as its parameter. we create a write method that will append a string to the widget’s current value. Then in the MyApp class, we create all the necessary widgets and redirect stdout to our RedirectText class. We also bind the button to our open_file method. This is where the action happens!
Here we create the file dialog and open a file. Then we read the file line by line and print it to stdout. If you try the example, you will see each line of text appear in the ScrolledText widget.
Wrapping Up
As you can see, redirecting stdout / stderr is pretty easy. I hope you will find many neat uses for this technique. Happy coding!
Related Reading
- wxPython – Redirecting stdout / stderr
Will this work if you’re writing to stdout from a different thread? I think it will be problematic then because the other thread will be manipulating the gui, which is usually preserved for the main thread is it not?
You are correct. This example is NOT thread-safe.You would have to do some additional research to find out what Tkinter’s thread-safe methods are.
I’m using root.after_idle to schedule the actual write and gui-update calls in the Tk gui event loop thread.
how come write method is never called still executing….
This is because we redirected stdout to the text control. Thus, if we print something to stdout, it gets redirected to our widget instead of a console.
I’ve tried lots of different solutions I found online, and this one is the easiest to implement in my simple program. Thank you, works beautifully!
Thanks! I’m glad it was helpful to you.