2010-05-12 11 views
11

Sto usando un modulo nella mia app Python che scrive un sacco di messaggi usando il modulo di registrazione. Inizialmente stavo usando questo in un'applicazione console ed è stato abbastanza facile ottenere l'output di registrazione da visualizzare sulla console usando un gestore di console. Ora ho sviluppato una versione GUI della mia app usando wxPython e mi piacerebbe mostrare tutti gli output di logging a un controllo personalizzato: un testo multi-linea. C'è un modo in cui posso creare un gestore di registrazione personalizzato in modo da poter reindirizzare tutti gli output di registrazione lì e visualizzare i messaggi di registrazione ovunque/comunque voglio - in questo caso, un'app wxPython.Come posso reindirizzare il registratore ad un wxPython textCtrl usando un gestore di registrazione personalizzato?

risposta

12

Creare Handler

import wx 
import wx.lib.newevent 

import logging 

# create event type 
wxLogEvent, EVT_WX_LOG_EVENT = wx.lib.newevent.NewEvent() 


class wxLogHandler(logging.Handler): 
    """ 
    A handler class which sends log strings to a wx object 
    """ 
    def __init__(self, wxDest=None): 
     """ 
     Initialize the handler 
     @param wxDest: the destination object to post the event to 
     @type wxDest: wx.Window 
     """ 
     logging.Handler.__init__(self) 
     self.wxDest = wxDest 
     self.level = logging.DEBUG 

    def flush(self): 
     """ 
     does nothing for this handler 
     """ 


    def emit(self, record): 
     """ 
     Emit a record. 

     """ 
     try: 
      msg = self.format(record) 
      evt = wxLogEvent(message=msg,levelname=record.levelname)    
      wx.PostEvent(self.wxDest,evt) 
     except (KeyboardInterrupt, SystemExit): 
      raise 
     except: 
      self.handleError(record) 

Poi, nel vostro controllo

self.Bind(EVT_WX_LOG_EVENT, self.onLogEvent) 

def onLogEvent(self,event): 
    ''' 
    Add event.message to text window 
    ''' 
    msg = event.message.strip("\r")+"\n" 
    self.logwindow.AppendText(msg) # or whatevery 
    event.Skip() 
+0

@Vinjay Sajip: la tua risposta non è sicura se gli eventi vengono registrati al di fuori del ciclo principale di wx. È più sicuro usare gli eventi wx per elaborare i dati da thread esterni. – iondiode

+0

Indubbiamente hai ragione, ma la mia risposta indica solo l'approccio da utilizzare piuttosto che offrire una soluzione completamente testata. –

3

È necessario creare un numero personalizzato logging.Handler e aggiungerlo al numero logging.Logger.

Dalla documentazione:

Handler oggetti sono responsabili spedizione dei adeguata registro messaggi (sulla base della gravità i messaggi di log) ai specificato destinazione del conduttore. Gli oggetti logger possono aggiungere zero o più oggetti di gestione allo stesso con un metodo addHandler() . Come scenario di esempio, un'applicazione potrebbe voler inviare tutti i messaggi di registro a un file di registro, tutti i messaggi di errore di errore o superiore a stdout, e tutti i messaggi di importanza critica a un indirizzo di posta elettronica . Questo scenario richiede tre singoli gestori in cui ogni gestore è responsabile dell'invio di messaggi di gravità specifica a una posizione specifica .

Vedere http://docs.python.org/library/logging.html#handler-objects per l'API Handler.

In particolare, è il metodo Handler.emit(record) che è possibile implementare per specificare la destinazione dell'output. Presumibilmente, dovresti implementarlo per chiamare TextCtrl.AppendText.

4

Ecco un semplice esempio di lavoro:

import logging 
import random 
import sys 
import wx 

logger = logging.getLogger(__name__) 

class WxTextCtrlHandler(logging.Handler): 
    def __init__(self, ctrl): 
     logging.Handler.__init__(self) 
     self.ctrl = ctrl 

    def emit(self, record): 
     s = self.format(record) + '\n' 
     wx.CallAfter(self.ctrl.WriteText, s) 

LEVELS = [ 
    logging.DEBUG, 
    logging.INFO, 
    logging.WARNING, 
    logging.ERROR, 
    logging.CRITICAL 
] 

class Frame(wx.Frame): 

    def __init__(self): 
     TITLE = "wxPython Logging To A Control" 
     wx.Frame.__init__(self, None, wx.ID_ANY, TITLE) 

     panel = wx.Panel(self, wx.ID_ANY) 
     log = wx.TextCtrl(panel, wx.ID_ANY, size=(300,100), 
          style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL) 
     btn = wx.Button(panel, wx.ID_ANY, 'Log something!') 
     self.Bind(wx.EVT_BUTTON, self.onButton, btn) 

     sizer = wx.BoxSizer(wx.VERTICAL) 
     sizer.Add(log, 1, wx.ALL|wx.EXPAND, 5) 
     sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5) 
     panel.SetSizer(sizer) 
     handler = WxTextCtrlHandler(log) 
     logger.addHandler(handler) 
     FORMAT = "%(asctime)s %(levelname)s %(message)s" 
     handler.setFormatter(logging.Formatter(FORMAT)) 
     logger.setLevel(logging.DEBUG) 

    def onButton(self, event): 
     logger.log(random.choice(LEVELS), "More? click again!") 

if __name__ == "__main__": 
    app = wx.PySimpleApp() 
    frame = Frame().Show() 
    app.MainLoop() 

Screenshot:

Screenshot of running script

Aggiornamento: Come indicato da iondiode, questo semplice script potrebbe presentare problemi se nella propria app sono presenti più thread, tutti i registri tramite tale gestore; idealmente solo un thread dell'interfaccia utente dovrebbe aggiornare l'interfaccia utente. Puoi utilizzare l'approccio suggerito per registrare l'evento utilizzando un evento personalizzato, come da sua risposta.

+0

è la registrazione di riga.Handler .__ init __ (self) corretta?È giusto passare dentro __init__? – piertoni

Problemi correlati