2010-05-12 31 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

相關問題