2010-05-12 11 views
11

Estoy usando un módulo en mi aplicación python que escribe muchos mensajes utilizando el módulo de registro. Inicialmente, estaba usando esto en una aplicación de consola y era bastante fácil obtener la salida de registro para mostrar en la consola usando un controlador de consola. Ahora he desarrollado una versión de GUI de mi aplicación usando wxPython y me gustaría mostrar todo el resultado de registro a un control personalizado - un textCtrl multilínea. ¿Existe alguna manera de que pueda crear un controlador de registro personalizado para que pueda redirigir todo el resultado de registro allí y mostrar los mensajes de registro donde sea/como yo quiera, en este caso, una aplicación wxPython.¿Cómo puedo redireccionar el registrador a wxPython textCtrl usando un controlador de registro personalizado?

Respuesta

12

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

Luego, en el control de

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: su respuesta no es segura si los eventos se registran fuera del bucle principal de wx. Es más seguro usar eventos wx para procesar datos de hilos externos. – iondiode

+0

Sin duda tiene razón, pero mi respuesta solo apunta al enfoque que se utilizará en lugar de ofrecer una solución totalmente probada en batalla. –

3

Tendrá que crear un logging.Handler personalizado y agregarlo a su logging.Logger.

De la documentación:

Handler objetos son responsables de el envío de los mensajes de registro apropiado (en base a la gravedad los mensajes de registro) a se especifica el destino del manipulador. Los objetos de registrador pueden agregar cero o más objetos de controlador a ellos mismos con un método addHandler() . Como una situación de ejemplo, una aplicación puede enviar todos los mensajes de registro a un archivo de registro, todos los mensajes de error de registro o superior a stdout, y todos los mensajes críticos a una dirección de correo electrónico . Este escenario requiere tres controladores individuales donde cada controlador es responsable de enviar mensajes de una gravedad específica a una ubicación específica .

Ver http://docs.python.org/library/logging.html#handler-objects para la API Handler.

En particular, es el método Handler.emit(record) que puede implementar para especificar el destino de la salida. Presumiblemente, implementaría esto para llamar al TextCtrl.AppendText.

4

Aquí es un simple ejemplo de trabajo:

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

Captura de pantalla:

Screenshot of running script

Actualización: Como señala iondiode, este script simple puede tener problemas si hay múltiples subprocesos en su aplicación, todos los inicios de sesión a través de dicho controlador; lo ideal es que solo un hilo de interfaz de usuario actualice la interfaz de usuario. Puede usar el enfoque sugerido para registrar el evento mediante un evento personalizado, según su respuesta.

+0

es el registro de línea.Handler .__ init __ (self) ¿correcto?¿Es correcto pasar auto dentro de __init__? – piertoni

Cuestiones relacionadas