2010-05-12 9 views
11

Ich benutze ein Modul in meiner Python-App, die eine Menge von Nachrichten mit dem Logging-Modul schreibt. Anfangs verwendete ich dies in einer Konsolenanwendung und es war ziemlich einfach, die Logging-Ausgabe mit einem Konsolen-Handler auf der Konsole anzuzeigen. Jetzt habe ich eine GUI-Version meiner App mit wxPython entwickelt und möchte die gesamte Logging-Ausgabe zu einem benutzerdefinierten Steuerelement anzeigen - eine mehrzeilige textCtrl. Gibt es eine Möglichkeit, einen benutzerdefinierten Logging-Handler zu erstellen, so dass ich die gesamte Logging-Ausgabe umleiten kann und die Logging-Nachrichten anzeigen kann, wo immer ich möchte - in diesem Fall eine wxPython-App.Wie kann ich den Logger mithilfe eines benutzerdefinierten Protokollierungshandlers in eine wxPython-textCtrl umleiten?

Antwort

12

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

Dann in Ihrer Kontrolle

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: Ihre Antwort ist nicht Thread sicher, wenn Ereignisse außerhalb von Wx Hauptschleife protokolliert werden. Es ist sicherer, wx-Ereignisse zu verwenden, um Daten von externen Threads zu verarbeiten. – iondiode

+0

Zweifellos hast du recht, aber meine Antwort zeigt nur auf den Ansatz, der verwendet wird, anstatt eine vollständig kampferprobte Lösung anzubieten. –

3

Sie müssen ein benutzerdefiniertes logging.Handler erstellen und es zu Ihrem logging.Logger hinzufügen.

Aus der Dokumentation:

Handler Objekte sind für verantwortlich die entsprechende Protokoll Nachrichten Dispatching (basierend auf den Schwere der Log-Meldungen) an den angegebenen Ziel absetzen. Logger-Objekte können null oder mehr Handler-Objekte mit einer Methode addHandler() zu selbst hinzufügen. Als ein Beispielszenario könnte eine Anwendung alle Protokollnachrichten an eine Protokolldatei senden, alle Protokollfehlermeldungen oder höher an stdout, und alle Nachrichten von kritisch an eine E-Mail-Adresse senden. Dieses Szenario erfordert drei einzelne Handler, wobei jeder Handler ist verantwortlich für das Senden von Nachrichten mit einem bestimmten Schweregrad an einen spezifischen Speicherort .

Siehe http://docs.python.org/library/logging.html#handler-objects für die Handler API.

Insbesondere ist es die Handler.emit(record) Methode, die Sie implementieren können, um das Ziel der Ausgabe anzugeben. Vermutlich würden Sie dies implementieren, um TextCtrl.AppendText zu rufen.

4

Hier ist ein einfaches Arbeitsbeispiel:

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

Update: Wie iondiode darauf hinweist, kann dieses einfache Skript Probleme haben, wenn mehrere Threads in Ihrer App vorhanden sind, die alle über einen solchen Handler protokollieren; Im Idealfall sollte nur ein UI-Thread die Benutzeroberfläche aktualisieren. Sie können den vorgeschlagenen Ansatz zum Protokollieren des Ereignisses mithilfe eines benutzerdefinierten Ereignisses gemäß seiner Antwort verwenden.

+0

ist die Zeile logging.Handler .__ init __ (selbst) korrekt?Ist es richtig, sich selbst in __init__ zu übergeben? – piertoni

Verwandte Themen