Как я могу перенаправить регистратор к wxPython textCtrl с помощью пользовательского обработчика входов?

Я использую модуль в своем приложении Python, которое пишет много сообщений с помощью регистрирующегося модуля. Первоначально я использовал это в консольном приложении, и было довольно легко заставить вывод входа отображаться на консоли с помощью обработчика консолей. Теперь я разработал версию GUI своего приложения с помощью wxPython, и я хотел бы отобразить весь регистрирующийся вывод к пользовательскому элементу управления — мультилиния textCtrl. Есть ли способ, которым я мог создать пользовательский обработчик входов, таким образом, я могу перенаправить весь вывод входа там и отобразить регистрирующиеся сообщения везде, где/однако я хочу — в этом случае, wxPython приложение.

11
задан Mridang Agarwalla 29 May 2014 в 09:42
поделиться

3 ответа

Создайте обработчик

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()
14
ответ дан 3 December 2019 в 05:33
поделиться

Вам нужно будет создать собственный logging.Handler и добавить его в свой logging.Logger .

Из документации:

Объекты обработчика отвечают за отправку соответствующих сообщений журнала (на основе серьезности сообщений журнала) обработчику указано место назначения. Объекты регистратора могут добавлять ноль или более объектов-обработчиков к самим себе с помощью метода addHandler () . В качестве примера сценария приложению может потребоваться отправить все сообщения журнала в файл журнала, все сообщения журнала об ошибках или выше в стандартный вывод, и все критические сообщения для адреса электронной почты . Для этого сценария требуются три отдельных обработчика, каждый из которых отвечает за отправку сообщений определенной важности в определенное место.

См. http://docs.python.org/library/logging.html#handler-objects для Handler API.

В частности, это метод Handler.emit (record) , который вы можете реализовать, чтобы указать место назначения вывода. Предположительно, вы бы реализовали это для вызова TextCtrl.AppendText .

3
ответ дан 3 December 2019 в 05:33
поделиться

Вот простой рабочий пример:

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. Вы можете использовать предложенный подход к регистрации события с помощью пользовательского события, как указано в его ответе.

4
ответ дан 3 December 2019 в 05:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: