如何使用自定义日志处理程序将记录器重定向到wxpythontextctrl?
我在python应用程序中使用了一个模块,该模块使用日志模块编写大量消息。最初我在控制台应用程序中使用它,使用控制台处理程序很容易在控制台上显示日志输出。现在,我已经使用wxPython开发了我的应用程序的GUI版本,我想将所有日志输出显示到一个自定义控件——一个多行textCtrl。有没有一种方法可以创建一个自定义日志处理程序,这样我就可以将所有日志输出重定向到那里,并在任何我想要的地方/以任何方式显示日志消息-在本例中,是一个wxPython应用程序。您需要创建一个自定义的如何使用自定义日志处理程序将记录器重定向到wxpythontextctrl?,python,logging,wxpython,Python,Logging,Wxpython,我在python应用程序中使用了一个模块,该模块使用日志模块编写大量消息。最初我在控制台应用程序中使用它,使用控制台处理程序很容易在控制台上显示日志输出。现在,我已经使用wxPython开发了我的应用程序的GUI版本,我想将所有日志输出显示到一个自定义控件——一个多行textCtrl。有没有一种方法可以创建一个自定义日志处理程序,这样我就可以将所有日志输出重定向到那里,并在任何我想要的地方/以任何方式显示日志消息-在本例中,是一个wxPython应用程序。您需要创建一个自定义的日志记录.hand
日志记录.handler
并将其添加到日志记录.Logger
中
从文件中:
处理程序
对象负责
发送适当的日志
消息(基于日志消息的
严重性)设置为处理程序的指定
目的地。记录器对象可以添加
零个或多个要删除的处理程序对象
使用addHandler()对自己进行处理
方法。作为一个示例场景
应用程序可能希望发送所有日志
发送到日志文件的消息,所有日志
发送到标准输出的错误或更高级别消息,
以及所有对
电子邮件地址。这种情况需要
三个单独的处理程序,每个
处理程序负责发送
将具有特定严重性的消息发送到
具体位置
有关处理程序
API,请参阅
特别是,可以实现Handler.emit(record)
方法来指定输出的目标。大概,您将实现此功能来调用TextCtrl.AppendText
创建处理程序
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()
下面是一个简单的工作示例:
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()
截图:
更新:正如IonDide指出的,如果应用程序中有多个线程,那么这个简单的脚本可能会有问题,所有这些线程都是通过这样的处理程序记录的;理想情况下,只有UI线程应该更新UI。根据他的回答,您可以使用建议的方法,通过使用自定义事件记录事件。@Vinjay Sajip:如果事件记录在wx主循环之外,则您的回答不是线程安全的。使用wx事件来处理来自外部线程的数据更安全。毫无疑问,你是对的,但我的答案只是指向要使用的方法,而不是提供一个经过全面战斗测试的解决方案。line logging.Handler.\uuuu init\uuuu(self)正确吗?在init中传递self是否正确?