2012-11-10 18 views
18

Gibt es da draußen ein Beispiel, wie man die Anmeldung in Python an einem Tkinter Text Widget einrichtet? Ich habe gesehen, dass dies in mehreren Apps verwendet wurde, aber ich kann nicht herausfinden, wie ich die Protokollierung auf etwas anderes als eine Protokolldatei lenken soll.Python Logging to Tkinter Text Widget

Antwort

9

Sie sollten logging.Handler Unterklasse, zB:

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

ich auf Yuris Idee gebaut, aber brauchte ein paar Änderungen, um die Dinge zum Laufen zu bringen:

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

Beachten Sie, dass der Zustand zurück Makeln und von "normal" zu "deaktiviert" ist erforderlich, um das Widget schreibgeschützt zu machen.

1

Aufbauend auf ford's Antwort, hier ist ein Scroll-Text-Widget, das das Protokoll schwänzt. Das Mitglied logging_handler ist, was Sie Ihrem Logger hinzufügen.

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

Auch auf Ford bauen, aber farbigen Text hinzufügen!

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

Zusätzlich zu den oben genannten Antworten: Auch wenn viele der vorgeschlagenen Lösungen für diese gibt es (hier und auch in this other thread), war ich ziemlich viel kämpfen, dies selbst arbeiten. Schließlich lief ich in this text handler class by Moshe Kaplan, die ein ScrolledText Widget verwendet (das ist wahrscheinlich einfacher als die ScrollBar-Methode).

Es hat einige Zeit gedauert, bis ich herausgefunden habe, wie man Moshes Klasse tatsächlich in einer Thread-Anwendung verwendet. Am Ende habe ich ein minimales Demo-Skript erstellt, das zeigt, wie alles funktioniert. Wie es für andere hilfreich sein könnte, teile ich es unten. In meinem speziellen Fall wollte ich auf sowohl die GUI und eine Textdatei protokollieren; Wenn Sie das nicht benötigen, entfernen Sie einfach den Dateinamen Attribut in 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 oben Code hier:

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

+0

Für diejenigen von Ihnen, die ** 'TclStackFree bekommen: falsche freePtr' ** Fehler, die Antwort oben löst dieses Problem. Die Verwendung von 'self.widget.after (0, function_to_execute)' stellt sicher, dass das Widget von dem Thread geändert wird, zu dem es gehört. – Felix

Verwandte Themen