Делая графическую обертку для некого инструмента на wxPython я столкнулся с надобностью вывода событий (простых логов) в окно wx.TextCtrl
для начала мне помогла вот эта статья. В дальнейшем развитии мне хотелось сделать настройку вывода логов, например инвертировать лог или окрасить особые события в процессе их вывода и способ выше уже мало годился, так как пришлось бы переписать практически весь код.
Список всех урок: Полный курс уроков по wxPython
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Методом изучения документации, я наткнулся на интересный метод wx.TextCtrl.AppendText(msg)
, который добавляет в окно принятую строку msg
к уже имеющимся.
Возьмем frame из статьи выше и избавимся от класса CustomConsoleHandler
и и что с ним связано.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import wx import random class MyPanel(wx.Panel): def __init__(self, parent): """Constructor""" # создадим панель в которую помести окно вывода и кнопку для генерации событий wx.Panel.__init__(self, parent) self.logText = wx.TextCtrl( self, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL | wx.TE_RICH) btn = wx.Button(self, label="Press Me") btn.Bind(wx.EVT_BUTTON, self.onPress) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.logText, 1, wx.EXPAND | wx.ALL, 5) sizer.Add(btn, 0, wx.ALL, 5) self.SetSizer(sizer) def onPress(self, event): """ Ивент который генерит сообщение по нажатию на кнопку """ random_list = ['ERROR: this is error\n', 'INFO: start program\n', 'DEBUG: debug info for developer\n'] self.outputPrint(random.choice(random_list)) def outputPrint(self, message): self.logText.AppendText(message) class MyFrame(wx.Frame): def __init__(self): """Constructor""" wx.Frame.__init__(self, None, title="Logging test") panel = MyPanel(self) self.Show() |
Добавим метод, который будет выводить в наше окно логов какой-то текст по нажатию кнопки и не забываем, что после message
стоит добавить \n
для переноса новой строки логов и итого получаем:
Инвертировать лог, можно добавив чекбокс wx.CheckBox
и создав список, в котором можно хранить все сообщения. Однако есть нюансы, перед каждым вызовом метода, необходимо очищать TextCtrl иначе, сообщения будут дублироваться и спамить. а также после цикла нужно переместить курсор вверх на последнее сообщение инвертированного лога, тут поможет wx.TextCtrl.SetInsertionPoint(0)
который помещает курсор TextCtrl
в нужное положение.
Для инверсии проверяем флажок при помощи метода IsChecked
и если он True
, то делаем reversed
нашего списка с сообщениями, таким образом вывод будет инвертирован.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# coding: utf-8 import wx import random class MyPanel(wx.Panel): def __init__(self, parent): """Constructor""" # создадим панель в которую помести окно вывода, чебокс инвертировать и кнопку для генерации событий wx.Panel.__init__(self, parent) # десь мы будем хранить все сообщения self.cache_msg = list() self.logText = wx.TextCtrl( self, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL | wx.TE_RICH) btn = wx.Button(self, label="Press Me") btn.Bind(wx.EVT_BUTTON, self.onPress) # чебокс self.check_box_invert = wx.CheckBox(self, wx.ID_ANY, 'Invert log', wx.DefaultPosition, wx.DefaultSize, 0) sizer = wx.BoxSizer(wx.VERTICAL) # горизонтальный сайзер в который мы поместим кнопку и чекбокс, что бы они были рядом hor_sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.logText, 1, wx.EXPAND | wx.ALL, 5) hor_sizer.Add(btn, 0, wx.ALL, 5) hor_sizer.Add(self.check_box_invert, 0, wx.ALL, 5) sizer.Add(hor_sizer, 0, wx.ALL, 5) self.SetSizer(sizer) def onPress(self, event): """ Ивент который генерит сообщение по нажатию на кнопку """ random_list = ['ERROR: this is error\n', 'INFO: start program\n', 'DEBUG: debug info for developer\n'] self.outputPrint(random.choice(random_list)) def outputPrint(self, message): # сохраним наше сообщение в списке self.cache_msg.append(message) # очистим окно от старых сообщений self.logText.Clear() # в зависимости от состояния флажка обном сообщения в окне # если флажок устновлен, то инвертируме лог (последнее сообщение будет вверху) if self.check_box_invert.IsChecked(): for msg in reversed(self.cache_msg): # определим цвет сообщения color = self.outputColored(str(msg)) # добавим его в окно self.logText.AppendText(msg) # окрасим вывод self.logText.SetForegroundColour(color) # перемещаем курсор в верхнее положение, иначе пользователь будет видеть нижнее сообщение # используем только если инвертировать лог включено self.logText.SetInsertionPoint(0) else: for msg in self.cache_msg: color = self.outputColored(str(msg)) self.logText.AppendText(msg) self.logText.SetForegroundColour(color) @staticmethod # метод определяет цвет вывода def outputColored(message): return wx.RED if message.split()[0] == 'ERROR:' else wx.BLACK class MyFrame(wx.Frame): def __init__(self): """Constructor""" wx.Frame.__init__(self, None, title="Logging test") panel = MyPanel(self) self.Show() def main(): app = wx.App(False) frame = MyFrame() app.MainLoop() if __name__ == "__main__": main() |
Также можно окрасить вывод в нужный цвет, если в лог упало событие определенного вида, для демонстрации добавим модуль рандом и создадим список событий которые будут генерировать наш лог. дополнительно понадобится метод, который будет определять цвет лога. Обратите внимание, что в данной реализации окрашиваться будет весь вывод, а не конкретная строка, но это можно исправить переделав вывод, это в другой раз разберем.
НЮАНС
Если логи будут приходить не из потока, а из процесса который крутится на бэкенде или в самом ui
, то сама программа может зависнуть и ждать ответа. Этот метод подойдет для логирования внутренних методов самого ui или логи вынесенные в отдельный поток.
ИТОГ
Для вывода небольшого кол-ва логов, можно использовать AppendText
, иначе стоит подумать насчет хендлера, так как вышеописанный способ не самый оптимальный. Вывод в окне можно окрасить в любой цвет, можно использовать как цвета от самого wx (wx.RED
, wx.GREEN
и т.д.) но и множества RGB (255, 0, 0) — красный, (0, 100, 0) — зеленый.
Благодарности
Данная статья была написана нашим уважаемым читателем Павел Вирекс. Спасибо активным читателям которые делятся своими знаниями со всеми.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»