2010-05-12 24 views
11

我在我的python应用程序中使用了一个模块,它使用日志记录模块写了很多消息。最初我在控制台应用程序中使用它,并且使用控制台处理程序使日志输出显示在控制台上非常简单。现在我使用wxPython开发了我的应用程序的GUI版本,并且我想将所有日志输出显示到自定义控件 - 多行textCtrl。有没有一种方法可以创建自定义日志记录处理程序,以便我可以将所有日志记录输出重定向到那里,并在任何地方显示日志消息/但是我希望 - 在这种情况下,是一个wxPython应用程序。如何使用自定义日志记录处理程序将记录器重定向到wxPython textCtrl?

回答

12

在你的控制创建处理程序

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) 

然后

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:如果事件在wx主循环之外被记录,你的回答不是线程安全的。使用wx事件处理来自外部线程的数据更安全。 – iondiode 2010-05-12 23:49:07

+0

毫无疑问,你是对的,但我的回答只是指出要使用的方法,而不是提供经过战场考验的完整解决方案。 – 2010-05-13 14:42:26

3

您将需要创建自定义logging.Handler并将其添加到您的logging.Logger

从文档:

Handler对象负责 调度相应的日志 消息(基于日志消息 严重性)的处理程序的规定 目的地。记录器对象可以使用addHandler() 方法将 零个或多个处理程序对象本身添加到 本身。举例来说, 应用程序可能希望将所有日志 消息发送到日志文件,所有日志文件中包含错误或更高的错误消息,标准输出, 以及所有对电子邮件地址至关重要的消息。这种情况下需要三个独立的处理程序,其中每个处理程序负责将特定严重性的消息发送到 特定位置。

请参阅http://docs.python.org/library/logging.html#handler-objectsHandler API。

特别是,您可以使用Handler.emit(record)方法来指定输出的目的地。据推测,你会执行这个调用TextCtrl.AppendText

4

这里有一个简单的工作示例:

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 of running script

更新:正如iondiode指出的,如果您的应用程序中有多个线程,通过这样的处理程序进行所有日志记录,则此简单脚本可能会有问题;理想情况下只有一个UI线程应该更新UI。根据他的回答,您可以使用建议的使用自定义事件记录事件的方法。

+0

是行logging.Handler .__ init __(self)是否正确?在__init__中传递自己是否正确? – piertoni 2016-01-19 07:16:28

相关问题