2012-11-10 24 views
18

Se uno là fuori ha un esempio di come impostare la registrazione in Python per un Tkinter testo Widget? Ho visto questo utilizzato in diverse app, ma non riesco a capire come indirizzare la registrazione a qualcosa di diverso da un file di registro.Python registrazione a Tkinter Testo Widget

risposta

9

Si dovrebbe sottoclasse logging.Handler, ad esempio:

import logging 
from Tkinter import INSERT 

class WidgetLogger(logging.Handler): 
    def __init__(self, widget): 
     logging.Handler.__init__(self) 
     self.widget = widget 

    def emit(self, record): 
     # Append message (record) to the widget 
     self.widget.insert(INSERT, record + '\n') 
6

ho costruito sull'idea di Yuri, ma bisogno di fare alcune modifiche per far funzionare le cose:

import logging 
import Tkinter as tk 

class WidgetLogger(logging.Handler): 
    def __init__(self, widget): 
     logging.Handler.__init__(self) 
     self.setLevel(logging.INFO) 
     self.widget = widget 
     self.widget.config(state='disabled') 

    def emit(self, record): 
     self.widget.config(state='normal') 
     # Append message (record) to the widget 
     self.widget.insert(tk.END, self.format(record) + '\n') 
     self.widget.see(tk.END) # Scroll to the bottom 
     self.widget.config(state='disabled') 

Nota che commutando lo stato indietro e avanti da 'normale' a 'disabilitato' è necessario per rendere il widget Text di sola lettura.

1

costruzione più avanti la risposta di Ford, ecco un widget di testo scorrevole che le code dei log. Il membro logging_handler è ciò che aggiungi al tuo registratore.

import logging 
from Tkinter import END, N, S, E, W, Scrollbar, Text 
import ttk 

class LoggingHandlerFrame(ttk.Frame): 

    class Handler(logging.Handler): 
     def __init__(self, widget): 
      logging.Handler.__init__(self) 
      self.setFormatter(logging.Formatter("%(asctime)s: %(message)s")) 
      self.widget = widget 
      self.widget.config(state='disabled') 

     def emit(self, record): 
      self.widget.config(state='normal') 
      self.widget.insert(END, self.format(record) + "\n") 
      self.widget.see(END) 
      self.widget.config(state='disabled') 

    def __init__(self, *args, **kwargs): 
     ttk.Frame.__init__(self, *args, **kwargs) 

     self.columnconfigure(0, weight=1) 
     self.columnconfigure(1, weight=0) 
     self.rowconfigure(0, weight=1) 

     self.scrollbar = Scrollbar(self) 
     self.scrollbar.grid(row=0, column=1, sticky=(N,S,E)) 

     self.text = Text(self, yscrollcommand=self.scrollbar.set) 
     self.text.grid(row=0, column=0, sticky=(N,S,E,W)) 

     self.scrollbar.config(command=self.text.yview) 

     self.logging_handler = LoggingHandlerFrame.Handler(self.text) 
0

Anche su ford ma aggiungendo testo colorato!

class WidgetLogger(logging.Handler): 
    def __init__(self, widget): 
     logging.Handler.__init__(self) 
     self.setLevel(logging.DEBUG) 
     self.widget = widget 
     self.widget.config(state='disabled') 
     self.widget.tag_config("INFO", foreground="black") 
     self.widget.tag_config("DEBUG", foreground="grey") 
     self.widget.tag_config("WARNING", foreground="orange") 
     self.widget.tag_config("ERROR", foreground="red") 
     self.widget.tag_config("CRITICAL", foreground="red", underline=1) 

     self.red = self.widget.tag_configure("red", foreground="red") 
    def emit(self, record): 
     self.widget.config(state='normal') 
     # Append message (record) to the widget 
     self.widget.insert(tk.END, self.format(record) + '\n', record.levelname) 
     self.widget.see(tk.END) # Scroll to the bottom 
     self.widget.config(state='disabled') 
     self.widget.update() # Refresh the widget 
5

Oltre alle risposte di cui sopra: anche se ci sono un sacco di soluzioni proposte per questa (qui e anche in this other thread), ho faticato un po 'per fare questo io stesso lavoro. Alla fine mi sono imbattuto in this text handler class by Moshe Kaplan, che utilizza un widget ScrolledText (che è probabilmente più facile rispetto al metodo ScrollBar).

Mi c'è voluto del tempo per capire come utilizzare effettivamente la classe di Moshe in un'applicazione filettato. Alla fine ho creato uno script demo minimale che mostra come far funzionare tutto. Come potrebbe essere d'aiuto agli altri, lo sto condividendo qui sotto. Nel mio caso particolare ho voluto accedere a sia l'interfaccia grafica e un file di testo; se non è necessario che basta rimuovere il nome del file attributo nel logging.basicConfig.

import time 
import threading 
import logging 
try: 
    import tkinter as tk # Python 3.x 
    import tkinter.scrolledtext as ScrolledText 
except ImportError: 
    import Tkinter as tk # Python 2.x 
    import ScrolledText 

class TextHandler(logging.Handler): 
    # This class allows you to log to a Tkinter Text or ScrolledText widget 
    # Adapted from Moshe Kaplan: https://gist.github.com/moshekaplan/c425f861de7bbf28ef06 

    def __init__(self, text): 
     # run the regular Handler __init__ 
     logging.Handler.__init__(self) 
     # Store a reference to the Text it will log to 
     self.text = text 

    def emit(self, record): 
     msg = self.format(record) 
     def append(): 
      self.text.configure(state='normal') 
      self.text.insert(tk.END, msg + '\n') 
      self.text.configure(state='disabled') 
      # Autoscroll to the bottom 
      self.text.yview(tk.END) 
     # This is necessary because we can't modify the Text from other threads 
     self.text.after(0, append) 

class myGUI(tk.Frame): 

    # This class defines the graphical user interface 

    def __init__(self, parent, *args, **kwargs): 
     tk.Frame.__init__(self, parent, *args, **kwargs) 
     self.root = parent 
     self.build_gui() 

    def build_gui(self):      
     # Build GUI 
     self.root.title('TEST') 
     self.root.option_add('*tearOff', 'FALSE') 
     self.grid(column=0, row=0, sticky='ew') 
     self.grid_columnconfigure(0, weight=1, uniform='a') 
     self.grid_columnconfigure(1, weight=1, uniform='a') 
     self.grid_columnconfigure(2, weight=1, uniform='a') 
     self.grid_columnconfigure(3, weight=1, uniform='a') 

     # Add text widget to display logging info 
     st = ScrolledText.ScrolledText(self, state='disabled') 
     st.configure(font='TkFixedFont') 
     st.grid(column=0, row=1, sticky='w', columnspan=4) 

     # Create textLogger 
     text_handler = TextHandler(st) 

     # Logging configuration 
     logging.basicConfig(filename='test.log', 
      level=logging.INFO, 
      format='%(asctime)s - %(levelname)s - %(message)s')   

     # Add the handler to logger 
     logger = logging.getLogger()   
     logger.addHandler(text_handler) 

def worker(): 
    # Skeleton worker function, runs in separate thread (see below) 
    while True: 
     # Report time/date at 2-second intervals 
     time.sleep(2) 
     timeStr = time.asctime() 
     msg = 'Current time: ' + timeStr 
     logging.info(msg) 

def main(): 

    root = tk.Tk() 
    myGUI(root) 

    t1 = threading.Thread(target=worker, args=[]) 
    t1.start() 

    root.mainloop() 
    t1.join() 

main() 

Github Gist link per codice di cui sopra qui:

https://gist.github.com/bitsgalore/901d0abe4b874b483df3ddc4168754aa

+0

Per quelli di voi ricevendo il ** 'TclStackFree: freePtr' scorretto ** errore, la risposta di cui sopra risolve questo. L'uso di 'self.widget.after (0, function_to_execute)' assicura che il widget sia modificato dal thread a cui appartiene. – Felix

Problemi correlati