2013-02-14 5 views
4

Après avoir passé du temps à rediriger la sortie stdout et la journalisation vers un widget de texte tkinter, j'ai décidé que j'avais besoin d'aide. Mon code est le suivant:Rediriger la sortie de l'enregistreur Python vers le widget tkinter

#!/usr/bin/env python 
from Tkinter import * 
import logging 
from threading import Thread 

class IODirector(object): 
    def __init__(self,text_area): 
     self.text_area = text_area 

class StdoutDirector(IODirector): 
    def write(self,str): 
     self.text_area.insert(END,str) 
    def flush(self): 
     pass 

class App(Frame): 

    def __init__(self, master): 
     self.master = master 
     Frame.__init__(self,master,relief=SUNKEN,bd=2) 
     self.start() 

    def start(self): 
     self.master.title("Test") 
     self.submit = Button(self.master, text='Run', command=self.do_run, fg="red") 
     self.submit.grid(row=1, column=2) 
     self.text_area = Text(self.master,height=2.5,width=30,bg='light cyan') 
     self.text_area.grid(row=1,column=1) 

    def do_run(self): 
     t = Thread(target=print_stuff) 
     sys.stdout = StdoutDirector(self.text_area) 
     t.start() 

def print_stuff(): 
    logger = logging.getLogger('print_stuff') 
    logger.info('This will not show') 
    print 'This will show' 
    print_some_other_stuff() 

def print_some_other_stuff(): 
    logger = logging.getLogger('print_some_other_stuff') 
    logger.info('This will also not show') 
    print 'This will also show' 

def main():  
    logger = logging.getLogger('main') 
    root = Tk() 
    app = App(root) 
    root.mainloop() 

if __name__=='__main__': 
    main() 

Je sais que l'on peut définir un nouveau gestionnaire d'enregistrement basé sur un widget de texte, mais je ne peux pas le faire fonctionner. La fonction "print_stuff" est juste une enveloppe autour de nombreuses fonctions ayant toutes leur propre configuration de logger. J'ai besoin d'aide pour définir un nouveau gestionnaire de journalisation qui est "global" afin qu'il puisse être instancié à partir de chacune des fonctions ayant leur propre enregistreur. Toute aide est très appréciée.

Répondre

2

Voici une réponse complètement révisée qui fait ce que vous voulez. J'ai essayé d'indiquer quelles lignes du code dans votre question étaient changées et où de nouvelles lignes ont été ajoutées.

Par défaut, à sys.stderr, les messages sorties intégrées logger.StreamHandler de classe gestionnaire afin de les réorienté sys.stdout le widget texte nécessite la construction d'un nouvel enregistreur avec un gestionnaire de console personnalisée mis en place pour le faire. Puisque vous voulez que ceci s'applique à tous les enregistreurs dans le module, ce paramètre doit être appliqué à l'enregistreur "racine" sans nom dont hériteront tous les autres enregistreurs nommés.

from Tkinter import * 
import logging 
from threading import Thread 

class IODirector(object): 
    def __init__(self, text_area): 
     self.text_area = text_area 

class StdoutDirector(IODirector): 
    def write(self, msg): 
     self.text_area.insert(END, msg) 
    def flush(self): 
     pass 

class App(Frame): 
    def __init__(self, master): 
     self.master = master 
     Frame.__init__(self, master, relief=SUNKEN, bd=2) 
     self.start() 

    def start(self): 
     self.master.title("Test") 
     self.submit = Button(self.master, text='Run', command=self.do_run, fg="red") 
     self.submit.grid(row=1, column=2) 
     self.text_area = Text(self.master, height=2.5, width=30, bg='light cyan') 
     self.text_area.grid(row=1, column=1) 

    def do_run(self): 
     t = Thread(target=print_stuff) 
     sys.stdout = StdoutDirector(self.text_area) 
     # configure the nameless "root" logger to also write   # added 
     # to the redirected sys.stdout         # added 
     logger = logging.getLogger()         # added 
     console = logging.StreamHandler(stream=sys.stdout)    # added 
     logger.addHandler(console)          # added 
     t.start() 

def print_stuff(): 
    logger = logging.getLogger('print_stuff') # will inherit "root" logger settings 
    logger.info('This will now show')         # changed 
    print 'This will show' 
    print_some_other_stuff() 

def print_some_other_stuff(): 
    logger = logging.getLogger('print_some_other_stuff') # will inherit "root" logger settings 
    logger.info('This will also now show')        # changed 
    print 'This will also show' 

def main(): 
    logging.basicConfig(level=logging.INFO) # enable logging   # added 
    root = Tk() 
    app = App(root) 
    root.mainloop() 

if __name__=='__main__': 
    main() 
+1

Merci pour votre réponse mais j'ai déjà un moyen de rediriger stdout dans mon code. Le problème est que l'enregistreur Python ne semble pas écrire sur stdout même si vous ajoutez spécifiquement stdout comme gestionnaire de l'enregistreur. Au moins, les messages de l'enregistreur ne sont pas inclus lorsque vous redirigez stdout. – user2073502

4

Juste pour vous assurer que je comprends bien:

Vous voulez imprimer les messages de journalisation à la fois à votre StdOut et widget texte Tkinter, mais l'enregistrement ne seront pas imprimés dans la console standard.

Si c'est votre problème, voici comment le faire.

d'abord faisons une console très simple Tkinter, il pourrait être un widget texte vraiment mais je suis, y compris pour l'exhaustivité:

class LogDisplay(tk.LabelFrame): 
"""A simple 'console' to place at the bottom of a Tkinter window """ 
    def __init__(self, root, **options): 
     tk.LabelFrame.__init__(self, root, **options); 

     "Console Text space" 
     self.console = tk.Text(self, height=10) 
     self.console.pack(fill=tk.BOTH) 

Maintenant, nous allons remplacer les gestionnaires de journalisation pour rediriger vers une console dans le paramètre et toujours imprimer automatiquement à STDout:

class LoggingToGUI(logging.Handler): 
""" Used to redirect logging output to the widget passed in parameters """ 
    def __init__(self, console): 
     logging.Handler.__init__(self) 

     self.console = console #Any text widget, you can use the class above or not 

    def emit(self, message): # Overwrites the default handler's emit method 
     formattedMessage = self.format(message) #You can change the format here 

     # Disabling states so no user can write in it 
     self.console.configure(state=tk.NORMAL) 
     self.console.insert(tk.END, formattedMessage) #Inserting the logger message in the widget 
     self.console.configure(state=tk.DISABLED) 
     self.console.see(tk.END) 
     print(message) #You can just print to STDout in your overriden emit no need for black magic 

Espérons que cela aide.

+0

Je recommande de supprimer l'appel de 'pack' dans la fonction __init__, sinon vous ne pouvez utiliser cette classe que dans un cadre qui utilise pack. En règle générale, votre code sera plus facile à utiliser si la fonction qui crée un widget est également responsable de sa mise en page, plutôt que de laisser le widget se déployer. –

+0

Lorsque j'utilise plusieurs widgets dans un LabelFrame pour créer un 'MegaWidget', je place habituellement le sous-widget dans l'init, de sorte que l'utilisateur n'a qu'à placer le LabelFrame où il veut et tout ce qu'il contient sera correctement placé. Je peux voir comment c'est uselss dans cet exemple bien que j'ai enlevé tout mais le widget de texte pour la simplicité. – Asics

+0

Oh, mon mauvais! J'ai échoué à voir le cadre dans le init. Oui, les widgets internes doivent être gérés en interne. Désolé pour la confusion. –

Questions connexes