我在我的python应用程序中使用了一个模块,该模块使用日志记录模块编写了大量消息。最初我在控制台应用程序中使用它,并且很容易使用控制台处理程序将日志记录输出显示在控制台上。现在我使用wxPython开发了我的应用程序的GUI版本,我想将所有日志输出显示到自定义控件 - 多行textCtrl。有没有办法我可以创建一个自定义日志记录处理程序,以便我可以重定向所有日志记录输出并显示日志消息,无论我喜欢哪里 - 在这种情况下,wxPython应用程序。
答案 0 :(得分:13)
创建处理程序
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()
答案 1 :(得分: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()
截图:
更新:正如iondiode指出的那样,如果您的应用中有多个线程,这个简单的脚本可能会出现问题,所有线程都通过这样的处理程序进行记录;理想情况下,只有UI线程才能更新UI。根据他的回答,您可以使用建议的方法使用自定义事件记录事件。
答案 2 :(得分:3)
您需要创建自定义logging.Handler
并将其添加到logging.Logger
。
来自文档:
Handler
个对象负责 调度适当的日志 消息(基于日志消息' 严重性)指定处理程序 目的地。记录器对象可以添加 零个或多个处理程序对象 他们自己使用addHandler() 方法。作为一个示例场景,一个 应用程序可能要发送所有日志 消息到日志文件,所有日志 stdout错误或更高的消息, 和所有关键的消息 电子邮件地址。此方案需要 三个单独的处理程序 处理程序负责发送 具有特定严重性的消息 具体位置。
请参阅http://docs.python.org/library/logging.html#handler-objects了解Handler
API。
特别是,您可以实现Handler.emit(record)
方法来指定输出的目标。据推测,您可以实现此方法来调用TextCtrl.AppendText
。