如何使用自定义日志处理程序将日志记录器重定向到wxPython textCtrl?

2024-05-20 13:43:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我在python应用程序中使用了一个模块,它使用日志模块编写了很多消息。最初我是在控制台应用程序中使用它的,使用控制台处理程序很容易将日志输出显示在控制台上。现在我已经使用wxPython开发了我的应用程序的GUI版本,我想将所有日志输出显示到一个自定义控件(一个多行textCtrl)。有没有一种方法可以创建一个自定义日志处理程序,这样我就可以将所有日志输出重定向到那里,并在任何地方/任何我想显示的地方显示日志消息——在本例中是一个wxPython应用程序。


Tags: 模块方法版本应用程序消息处理程序地方wxpython
3条回答

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

从文档中:

Handler objects are responsible for dispatching the appropriate log messages (based on the log messages’ severity) to the handler’s specified destination. Logger objects can add zero or more handler objects to themselves with an addHandler() method. As an example scenario, an application may want to send all log messages to a log file, all log messages of error or higher to stdout, and all messages of critical to an email address. This scenario requires three individual handlers where each handler is responsible for sending messages of a specific severity to a specific location.

有关HandlerAPI,请参见http://docs.python.org/library/logging.html#handler-objects

特别是,可以实现Handler.emit(record)方法来指定输出的目标。大概,您可以实现这个来调用TextCtrl.AppendText

下面是一个简单的工作示例:

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。根据他的回答,您可以使用建议的方法,通过使用自定义事件来记录事件。

创建处理程序

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

相关问题 更多 >