Перенаправление stdout/stderr — wxPython #40

Довольно часто во время использования wxPython вам нужно перенаправить stdout или stderr в текстовый контроль. Может случиться такое, что вы запустите отдельное приложение с помощью wxPython, но вам хотелось бы видеть его исходящие данные в режиме реального времени. Я лично попадал в аналогичную ситуацию несчётное количество раз, так что могу назвать её весьма распространённой. Существует несколько отличных методов перенаправления stdout.

Метод thread-safe

Первым фрагментом необходимого нам кода является класс, который мы можем использовать для того, чтобы вычленить пишущее API TextCtrl. Вот довольно стандартный пример.

Обратите внимание на то, что в данном классе используется лишь один метод (помимо метода инициализации, разумеется). Это позволяет нам записывать текст из stdout или stderr в текстовый контроль. Стоит отметить, что этот метод записи не является thread-safe. Если вам нужно перенаправить текст из thread, нужно немного видоизменить утверждение write следующим образом:

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Теперь, когда мы знаем, как записать stdout в TextCtrl, давайте попробуем самостоятельно написать немного кода, позволяющего собрать все части воедино. Вы также можете добавить следующий код в файл, содержащий класс, который был написан нами только что. Когда вы запустите код, то увидите приложение, которое выглядит примерно так:

Перенаправление stdout/stderr - wxPython #40

В коде, расположенном вверху, я создал мультистрочный текстовый контроль, доступный только для чтения, и кнопку, главной задачей которой является вывод текста на stdout. Я добавил их в BoxSizer, чтобы защитить виджеты от напяливания друг поверх друга и для того, чтобы не возникало проблем с изменением размера рамки. Затем я инициирую класс RedirectText, пропуская его через инстанцию моего текстового контроля. Наконец, я настраиваю stdout в инстанцию RedirectText, redir (например, sys.stdout=redir).

Если вы хотите перенаправить stderr, просто добавьте следующую строку после sys.stdout=redir: sys.stderr=redir.

Вы можете улучшить данный пример изменив цвета кода, поступающего из stdout и stderr для того, чтобы быстро их различать. Эту задачу я оставил для вас, чтобы вы могли поупражняться.

Метод Non Thread-safe

Если вам не нужно беспокоится о тредах, которые пишутся в ваш TextCtrl, тогда вы можете немного упростить код благодаря тому, что TextCtrl имеет собственный метод write. Это значит, что вам больше не нужно будет использовать класс, который следует за пишущим API TextCtrl. Однако, это накладывает определённые ограничения, ввиду того, что метод write больше не встраивается в threadsafe метод wxPython, который называется: wx.CallAfter. Ладно, давайте продолжим и погрузимся в код:

Вы заметите, что код, расположенный выше, больше не ссылается на класс RedirectText, так как в данном случае он нам не нужен. Я почти уверен, что, если вы будете использовать threads, использование подобного подхода не будет расцениваться как threadsafe. Вам нужно переопределить метод write для TextCtrl способом, который мы обсуждали прежде, чтобы сделать его безопасным. Отдельная благодарность читателям моего блога, один из которых и натолкнул меня на эту идею.

В то же время, этот код не будет работать в wxPython Phoenix, так как wx.TextCtrl больше не имеет метода write(). Поэтому нам нужно написать версию кода, которая будет работать в Phoenix. Давайте займёмся этим!

Чтобы создать версию, которая будет работать на Phoenix, нам нужно превратить wx.TextCtrl в подкласс и создать собственный метод write(). Это нужно потому, что мы хотим заставить текстовый контроль вести себя так, будто он является файлоподобным объектом. Тогда мы сможем перенаправить stdout в него так, как положено. Если вы чётко следовали примеру, то заметите, что данный код работает точно также, как и пример, написанный нами для версии Classic.

Итоги

В данной статье мы рассмотрели несколько интересных тем. Мы научились перенаправлять stdout методами threadsafe и non threadsafe. Мне попадалось несколько проектов, в которых нужно было выполнить эту задачу, и все методы, перечисленные в данной статье, сработали как следует. Вы можете взять эту информацию и объединить её с информацией из других статей, чтобы перенаправлять stdout в ваши виджеты и лог-файлы.