Сегодня разбираем материал blog.pythonlibrary.org о теме «Textual — создаём пользовательский чекбокс». Материал полезен тем, кто хочет быстро понять суть темы и перевести идеи в прикладные действия.
Textual — это пакет для создания пользовательских интерфейсов на Python, позволяющий разрабатывать графические интерфейсы непосредственно в терминале без браузеров и оконных фреймворков

В Textual есть множество виджетов, и в этом руководстве мы сосредотачиваемся на чекбоксе
Чекбоксы служат для выбора между двумя состояниями. Они возвращают True, если отмечены, и False, если не отмечены. Чекбокс может служить визуальным индикатором включённой или отключённой опции
В этом руководстве вы узнаете:
- Как создать стандартный чекбокс
- Как настроить виджет чекбокса
- Как создать стандартный чекбокс
- Почему стандартный чекбокс не всегда подходит
- Как настроить виджет чекбокса
- Как добавить рамку через Textual CSS
- Типичные ошибки при создании пользовательского чекбокса
- Как применить эти знания к другим виджетам
- Подведение итогов
- Узнать больше
- Ответы на эти вопросы могут быть для вас полезными
Как создать стандартный чекбокс
Начинайте с виджета по умолчанию, чтобы проверить, подходит ли он вам. Откройте свою любимую Python IDE и создайте файл с кодом:
from textual.app import App, ComposeResult
from textual.containers import VerticalScroll
from textual.widgets import Checkbox
class CheckboxApp(App[None]): def compose(self) -> ComposeResult: with VerticalScroll(): yield Checkbox("Mike")
def on_checkbox_changed(self, event: Checkbox.Changed) -> None: self.notify(f"Checkbox value: {event.value}")
if __name__ == "__main__": CheckboxApp().run()
Приведённый выше пример основан на примере кода из документации Textual. Использовать контейнер VerticalScroll здесь не обязательно, но это удобно при добавлении серии виджетов в один контейнер
Основное внимание следует уделить самому Checkbox. Здесь вы просто возвращаете его из compose() с помощью yield, а затем перехватываете событие Checkbox.Changed. Когда событие срабатывает, вы отображаете пользователю уведомление о том, что он изменил значение чекбокса
Этот код создаст чекбокс с меткой и символом «X» в состоянии по умолчанию.
Однако если вы хотите, чтобы границы виджета были всегда видны, нужно добавить рамку к виджету
Но что если вы хотите, чтобы чекбокс был полностью пустым, а не отображал затемнённый «X»? Именно этому вы научитесь в следующем разделе.
По этой теме полезно отдельно посмотреть Составьте еженедельное расписание занятий, чтобы расширить контекст и сравнить подходы.
Почему стандартный чекбокс не всегда подходит
Если у вас фон по умолчанию или тёмный фон, затемнённый символ может стать проблемой с контрастностью.
Многие GUI-инструментарии отображают чекбокс пустым в состоянии False, поэтому поведение Textual, показывающее затемнённый «X», может сбивать с толку пользователей, привыкших к классическим интерфейсам.
Чтобы изменить отображаемый символ или сделать пустой бокс, как в других инструментариях, вам нужно научиться настраивать виджет. Создание пользовательского виджета в Textual очень просто
Как настроить виджет чекбокса
Создайте новый файл Python в вашей IDE и введите следующий код:
class CustomCheck(Checkbox): BUTTON_INNER = " "
def toggle(self) -> None: if self.value: CustomCheck.BUTTON_INNER = " " else: CustomCheck.BUTTON_INNER = "X" self.value = not self.value return self
class CheckboxApp(App[None]): #CSS_PATH = "checkbox.tcss" def compose(self) -> ComposeResult: check = CustomCheck("Mike") with VerticalScroll(): yield check
Когда вы хотите настроить уже существующий виджет в Textual, вы почти всегда будете создавать его подкласс (subclass). Здесь вы создаёте подкласс класса Checkbox, чтобы получить новый класс CustomCheck. Затем вы переопределяете атрибут класса BUTTON_INNER, а также метод toggle().
Если заглянуть в исходный код Textual, можно обнаружить, что BUTTON_INNER по умолчанию равен «X», поэтому здесь вы устанавливаете его по умолчанию в пустую строку. Затем вы обновляете toggle() так, чтобы «X» появлялся при отметке чекбокса и исчезал при снятии отметки.
Другое изменение — использование нового класса виджета в коде вашего приложения: вместо стандартного Checkbox вы создаёте экземпляр CustomCheck.
Как добавить рамку через Textual CSS
Чтобы увеличить видимость виджета, вы можете добавить рамку с помощью Textual CSS (каскадных таблиц стилей для Textual). Создайте новый файл с именем checkbox.tcss и добавьте в него следующий код:
CustomCheck { border: round green;
}
Убедитесь, что вы сохранили этот файл в той же папке, что и ваш файл Python.
Теперь раскомментируйте строку #CSS_PATH = "checkbox.tcss" в классе CheckboxApp, убрав символ #. После этого Textual подхватит стили автоматически.
При запуске этого кода вы увидите следующее: чекбокс с зелёной скруглённой рамкой, пустой по умолчанию и отображающий «X» при активации.
Отлично! Теперь у вас есть пользовательский чекбокс с нужным поведением и визуальным оформлением.
Типичные ошибки при создании пользовательского чекбокса
Работая с подобными задачами, я замечал несколько ошибок, которые встречаются чаще всего.
Файл стилей не найден. Если вы указали CSS_PATH, но файл .tcss лежит в другой папке, Textual выдаст ошибку при запуске. Убедитесь, что путь указан корректно или используйте абсолютный путь
Атрибут BUTTON_INNER изменяется глобально. Поскольку BUTTON_INNER — это атрибут класса, а не экземпляра, его изменение через CustomCheck.BUTTON_INNER затронет все экземпляры этого класса одновременно. Если вам нужно несколько независимых чекбоксов, стоит переработать логику и хранить состояние символа на уровне экземпляра.
Метод toggle() не вызывает родительский класс. Когда вы переопределяете toggle(), убедитесь, что понимаете, какую логику родительского класса вы заменяете. В данном примере вся логика переключения написана заново, что намеренно — но в более сложных случаях это может привести к потере функциональности.
Отсутствие рамки делает виджет малозаметным. Без явного стиля чекбокс может сливаться с фоном терминала. Добавление border через .tcss — простой способ сделать виджет визуально различимым.
Как применить эти знания к другим виджетам
Подход, который вы использовали для Checkbox, универсален для всей экосистемы Textual. Я применяю его каждый раз, когда стандартный виджет не покрывает нужное поведение: создаю подкласс, нахожу нужный атрибут или метод в исходном коде и переопределяю его. На моём опыте это работает одинаково надёжно как для простых виджетов вроде чекбокса, так и для более сложных компонентов.
Чтобы успешно модифицировать любой виджет Textual, нужно внимательно изучить его исходный код. Репозиторий Textual на GitHub хорошо структурирован: каждый виджет — отдельный файл, атрибуты класса вынесены наверх и легко читаются.
Вот общий алгоритм:
- Найдите нужный виджет в исходном коде Textual
- Определите атрибуты класса, которые управляют внешним видом или поведением
- Найдите методы, которые реализуют нужную вам логику
- Создайте подкласс и переопределите только то, что требует изменений
- Протестируйте поведение на нескольких экземплярах виджета
Возможно, вам придётся пройти через несколько итераций, чтобы получить именно то, что вы хотите. Это нормальная часть процесса — не стоит ожидать идеального результата с первой попытки
Подведение итогов
Textual — это отличный способ создавать красивые пользовательские интерфейсы в терминале. В этом руководстве вы узнали, как создать обычный виджет чекбокса, а затем научились создавать пользовательский чекбокс с нужным поведением и визуальным стилем.
Вы можете применить эти знания для настройки других виджетов в Textual. Вам нужно будет внимательно изучить код виджета, чтобы понять, как он работает, и успешно его модифицировать.
Не сдавайтесь — в конце концов вы добьётесь своего, и результат будет стоить потраченного времени
Узнать больше
Если эта статья показалась вам интересной и вы хотите узнать больше о Textual, ознакомьтесь со следующими ресурсами:
- Сайт Textual
- Creating TUI Applications with Textual and Python (книга)
Ответы на эти вопросы могут быть для вас полезными
Можно ли использовать несколько пользовательских чекбоксов одновременно?
Да, но нужно учитывать, что BUTTON_INNER — атрибут класса, а не экземпляра. Если вам нужны независимые чекбоксы с разным состоянием символа, стоит хранить символ на уровне экземпляра через self._inner и переопределить соответствующий метод рендеринга.
Обязательно ли использовать файл .tcss для стилизации?
Нет. Textual позволяет задавать стили и через атрибут DEFAULT_CSS прямо внутри класса виджета. Файл .tcss удобен для больших приложений, где стили нужно вынести отдельно и переиспользовать.
Что произойдёт, если не переопределить метод toggle()?
Стандартный toggle() переключит значение self.value, но символ BUTTON_INNER останется тем, что вы задали по умолчанию — пустой строкой. Чекбокс будет переключаться, но визуально оставаться пустым в обоих состояниях.
Можно ли изменить символ чекбокса на что-то отличное от «X»?
Да. В методе toggle() вы можете задать любой символ или даже эмодзи вместо "X". Главное — убедиться, что символ корректно отображается в вашем терминале и не нарушает выравнивание виджета.
Работает ли этот подход с другими виджетами Textual, например с кнопками или переключателями?
Да, принцип создания подкласса и переопределения атрибутов и методов универсален для всех виджетов Textual. Конкретные атрибуты и методы будут отличаться — их нужно искать в исходном коде соответствующего виджета.



