Cómo redirigir las declaraciones de impresión al widget de texto Tkinter

Tengo un progtwig Python que realiza un conjunto de operaciones e imprime la respuesta en STDOUT. Ahora estoy escribiendo una GUI que llamará a ese código ya existente y quiero imprimir el mismo contenido en la GUI en lugar de STDOUT. Usaré el widget de Texto para este propósito. No quiero modificar mi código existente que realiza la tarea (este código también lo utilizan otros progtwigs).

¿Puede alguien indicarme cómo puedo usar esta definición de tarea existente y usar su resultado STDOUT e insertarlo en un widget de texto? En el progtwig GUI principal, quiero llamar a esta definición de tarea e imprimir sus resultados a STDOUT. ¿Hay alguna manera de usar esta información?

Probablemente pueda resolver esto reemplazando sys.stdout con su propio objeto tipo archivo que escribe en el widget de texto.

Por ejemplo:

 import Tkinter as tk import sys class ExampleApp(tk.Tk): def __init__(self): tk.Tk.__init__(self) toolbar = tk.Frame(self) toolbar.pack(side="top", fill="x") b1 = tk.Button(self, text="print to stdout", command=self.print_stdout) b2 = tk.Button(self, text="print to stderr", command=self.print_stderr) b1.pack(in_=toolbar, side="left") b2.pack(in_=toolbar, side="left") self.text = tk.Text(self, wrap="word") self.text.pack(side="top", fill="both", expand=True) self.text.tag_configure("stderr", foreground="#b22222") sys.stdout = TextRedirector(self.text, "stdout") sys.stderr = TextRedirector(self.text, "stderr") def print_stdout(self): '''Illustrate that using 'print' writes to stdout''' print "this is stdout" def print_stderr(self): '''Illustrate that we can write directly to stderr''' sys.stderr.write("this is stderr\n") class TextRedirector(object): def __init__(self, widget, tag="stdout"): self.widget = widget self.tag = tag def write(self, str): self.widget.configure(state="normal") self.widget.insert("end", str, (self.tag,)) self.widget.configure(state="disabled") app = ExampleApp() app.mainloop() 

En Python, cuando llamas a print (‘examplestring’), estás llamando indirectamente a sys.stdout.write (‘examplestring’):

 from tkinter import * root=Tk() textbox=Text(root) textbox.pack() button1=Button(root, text='output', command=lambda : print('printing to GUI')) button1.pack() 

Método 1: Imprimir en GUI

 def redirector(inputStr): textbox.insert(INSERT, inputStr) sys.stdout.write = redirector #whenever sys.stdout.write is called, redirector is called. root.mainloop() 

De hecho llamamos imprimir – (callsfor) -> sys.stdout.write – (callsfor) -> redirector

Método 2: Escribir un decorador: imprima en CLI y GUI

 def decorator(func): def inner(inputStr): try: textbox.insert(INSERT, inputStr) return func(inputStr) except: return func(inputStr) return inner sys.stdout.write=decorator(sys.stdout.write) #print=decorator(print) #you can actually write this but not recommended root.mainloop() 

Lo que hace un decorador es en realidad asignar la func sys.stdout.write a func inner

 sys.stdout.write=inner 

y func inner agrega una línea de código adicional antes de devolver la llamada a sys.stdout.write

Esto es de alguna manera la actualización de la función más antigua sys.stdout.write para tener una nueva característica. Notará que usé un bash, excepto que si hay algún error al imprimir en el cuadro de texto, al menos conservaría la función original de sys.stdout.write a la CLI

Método 3: el ejemplo de Bryan Oakley

 ... sys.stdout = TextRedirector(self.text, "stdout") ... class TextRedirector(object): def __init__(self, widget, tag="stdout"): self.widget = widget self.tag = tag def write(self, str): self.widget.configure(state="normal") self.widget.insert("end", str, (self.tag,)) self.widget.configure(state="disabled") 

Lo que hizo fue que asignó sys.stdout a Class TextRedirector con un método .write (str)

así que llamar a print (‘string’) -calls for-> sys.stdout.write (‘string’) -callsfor-> TextRedirector.write (‘string’)

Puede llamar al progtwig CLI utilizando subprocess.Popen , tomar la salida estándar que produce y mostrarla en el widget de texto.

Algo a lo largo de las líneas de (no probado):

 import subprocess with subprocess.Popen(your_CLI_program, stdout=subprocess.PIPE) as cli line = cli.stdout.readline() #process the output of your_CLI_program print (line) 

Tenga en cuenta que esto se bloqueará hasta que el progtwig CLI termine de ejecutarse, lo que congelará su GUI. Para evitar el locking, puede poner este código en un threading.Thread y deje que la GUI se actualice mientras espera que el subproceso finalice.

La función que normalmente se imprime en la salida estándar debería colocar el texto en el widget de texto.