2012-06-01 12 views
5

Estoy intentando redirigir stdout a un widget de Etiqueta. El objetivo es "imprimir" en la etiqueta todas las impresiones de Python que están en mi script.¿Cómo redirigir "stdout" a un widget de etiqueta?

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() 

Respuesta

4

La razón por la que no está viendo el conjunto del texto es que está correctamente configurado para una fracción de segundo y luego establecer de inmediato en blanco. Esto se debe a que print está enviando una nueva línea a stdout después de las instrucciones de impresión. Aquí hay una versión modificada que se agrega a la etiqueta en lugar de sobrescribirla para cada declaración de impresión.

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

¡Eres increíble! ¡Esto está funcionando perfectamente! Gracias por su ayuda, no sabía que la impresión siempre está enviando una nueva línea a la salida estándar después de la declaración impresa, es bueno saber ;-) (No puedo votar porque no tengo más de 15 reputación, pero tan pronto como lo tenga, votaré por usted;)) –

3

Realicé una clase que copia las llamadas de escritura de stdout a un widget de 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: He recibido una downvote, por lo que aquí es la versión actualizada de uno. Utilizo esto en un widget Label y las funciones print() aparecen sin problemas en mi widget. Además, como característica adicional si paso None 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 adjunté un control deslizante al widget. Voy a publicar una demo pronto.

Cuestiones relacionadas