¿Cómo redirigir “stdout” a un widget de etiqueta?

Estoy tratando de redireccionar stdout a un widget de etiqueta. El objective es “imprimir” en la etiqueta todas las impresiones de Python que se encuentran en mi script.

Pero cuando hago clic en BUTTON1 no pasa nada …

Aquí está mi código:

 from Tkinter import * import sys import tkMessageBox class App: def __init__(self, master): self.frame = Frame(master, borderwidth=5, relief=RIDGE) self.frame.grid() class IORedirector(object): def __init__(self,TEXT_INFO): self.TEXT_INFO = TEXT_INFO class StdoutRedirector(IORedirector): def write(self,str): self.TEXT_INFO.config(text=str) self.TEXT_HEADER = self.text_intro = Label(self.frame, bg="lightblue",text="MY SUPER PROGRAMM") ## HEADER TEXT self.TEXT_HEADER.grid(row=0, column=0, columnspan=2, sticky=W+E+N+S) self.MENU = Frame(self.frame, borderwidth=5, relief=RIDGE, height=12) self.MENU.grid(row=1, column=0, sticky=N) self.button = Button(self.MENU, text="QUIT", fg="red", bg="red", command=self.frame.quit) self.button.grid(row=4, column=0) self.BUTTON1 = Button(self.MENU, text="BUTTON1", command=self.BUTTON1_CMD) self.BUTTON1.grid(row=0, column=0,sticky=W+E) self.TEXT_INFO = Label(self.frame, height=12, width=40, text="I WANT TO SEE THE STDOUT OUTPUT HERE", bg="grey",borderwidth=5, relief=RIDGE) self.TEXT_INFO.grid(row=1, column=1) sys.stdout = StdoutRedirector(self.TEXT_INFO) def BUTTON1_CMD(self): print "TEST NUMBER ONE" print "TEST NUMBER TWO" root = Tk() app = App(root) root.mainloop() 

La razón por la que no ve el conjunto de texto es que está configurado correctamente por una fracción de segundo y luego se establece en blanco. Esto se debe a que la impresión está enviando una nueva línea a la salida estándar después de las declaraciones de impresión. Aquí hay una versión modificada que se agrega a la etiqueta en lugar de sobrescribirla para cada statement de impresión.

  class StdoutRedirector(IORedirector): def write(self,str): self.TEXT_INFO.config(text=self.TEXT_INFO.cget('text') + str) 

Hice una clase que copia las llamadas de escritura estándar a un widget tkinter, ya sea una etiqueta o un texto. Funciona para mí en Python3.3.1 / WindowsXp .:

 import sys class StdoutToWidget: ''' Retrieves sys.stdout and show write calls also in a tkinter widget. It accepts widgets which have a "text" config and defines their width and height in characters. It also accepts Text widgets. Use stop() to stop retrieving. You can manage output height by using the keyword argument. By default the class tries to get widget\'s height configuration and use that. If that fails it sets self.height to None which you can also do manually. In this case the output will not be trimmed. However if you do not manage your widget, it can grow vertically hard by getting more and more inputs. ''' # Inspired by Jesse Harris and Stathis # http://stackoverflow.com/a/10846997/2334951 # http://stackoverflow.com/q/14710529/2334951 # TODO: horizontal wrapping # make it a widget decorator (if possible) # height management for Text widget mode def __init__(self, widget, height='default', width='default'): self._content = [] self.defstdout = sys.stdout self.widget = widget if height == 'default': try: self.height = widget.cget('height') except: self.height = None else: self.height = height if width == 'default': try: self.width = widget.cget('width') except: self.width = None else: self.width = width def flush(self): ''' Frame sys.stdout's flush method. ''' self.defstdout.flush() def write(self, string, end=None): ''' Frame sys.stdout's write method. This method puts the input strings to the widget. ''' if string is not None: self.defstdout.write(string) try: last_line_last_char = self._content[-1][-1] except IndexError: last_line_last_char = '\n' else: if last_line_last_char == '\n': self._content[-1] = self._content[-1][:-1] if last_line_last_char != '\n' and string.startswith('\r'): self._content[-1] = string[1:] elif last_line_last_char != '\n': self._content[-1] += string elif last_line_last_char == '\n' and string.startswith('\r'): self._content.append(string[1:]) else: self._content.append(string) if hasattr(self.widget, 'insert') and hasattr(self.widget, 'see'): self._write_to_textwidget() else: self._write_to_regularwidget(end) def _write_to_regularwidget(self, end): if self.height is None: self.widget.config(text='\n'.join(self.content)) else: if not end: content = '\n'.join(self.content[-self.height:]) else: content = '\n'.join(self.content[-self.height+end:end]) self.widget.config(text=content) def _write_to_textwidget(self): self.widget.insert('end', '\n'.join(self.content)) self.widget.see('end') def start(self): ''' Starts retrieving. ''' sys.stdout = self def stop(self): ''' Stops retrieving. ''' sys.stdout = self.defstdout @property def content(self): c = [] for li in self._content: c.extend(li.split('\n')) if not self.width: return c else: result = [] for li in c: while len(li) > self.width: result.append(li[:self.width]) li = li[self.width:] result.append(li) return result @content.setter def content(self, string): self._content = string.split('\n') @property def errors(self): return self.defstdout.errors @property def encoding(self): return self.defstdout.encoding 

EDIT1: recibí un voto negativo, así que aquí está el actualizado. Lo uso en un widget de tags y las funciones de impresión () aparecen sin problemas en mi widget. Además, como una característica adicional si paso Ninguno a la llamada de escritura y digamos -1 como argumento final, entonces no mostrará la última línea (cuidado con la indexación). Uso esto porque adjunto un control deslizante al widget. Publicaré una demo pronto.