Вступление
Всем доброго дня! В предыдущей статье Kawai.Focus - приложение для фокусировки внимания (часть 11):
- Обновлён дизайн экрана «Таймеры» в Material Design на KivyMD 2.0.0;
- Добавлена возможность переключать темы.
Сегодня я продолжаю обновлять дизайн приложения в стиле Material Design. В этот раз займусь боковой панелью навигации и добавлю несколько кнопок на экран «Таймеры».
Заваривайте чай, доставайте вкусняшки — пора “открыть меню с блюдами из томатов”! 🍅
Дизайн панели навигации
Мне нужна панель навигации, которая позволит удобнее управлять приложением. Я хочу вывести на ней кнопки меню.
Подбор образцов
Какие есть вариант в KivyMD?
AppBar (application bar) в языке UI — это панель в верхней части экрана приложения, которая обычно содержит:
- Заголовок (название экрана или раздела);
- Навигацию (кнопку «Назад», меню-бургер, вкладки);
- Действия (иконки поиска, уведомлений, профиля и т.п.).
Образец appbar:

О данном образце я уже писал в статье KivyMD 2.0.0 — путеводитель по Material Design для Kivy, в которой я делал путеводитель по Material Disign в Kivy. AppBar нужен для того что-бы убрать на неё кнопки навигации. В эту панель можно поместить иконку гамбургер, которая выглядит как три вертикальных полоски в левом верхнем углу экрана. На данный момент иконка гамбургер хранит настройки тем.
Рекомендации:
- Согласно Material Design, навигационная иконка (гамбургер-меню) размещается в крайнем левом положении верхнего бара. Это правило является стандартом для всех платформ и поддерживается как Android, так и KivyMD, где иконка является частью компонента
MDTopAppBar; - Между гамбургер-иконкой и заголовком экрана должны быть стандартные отступы — 16 dp согласно спецификациям Material Design. Эти отступы обеспечивают визуальную гармонию и соответствуют эргономическим требованиям для удобства восприятия.
NavigationRail в UI — это вертикальная панель навигации, которая служит альтернативой панели (NavigationBar) и позволяет пользователю переключаться между основными разделами приложения. Она особенно полезна на широких экранах, планшетах и десктопах, где есть место для вертикальной структуры.
Основные характеристики:
- Располагается слева или справа экрана;
- Содержит иконки и/или текстовые подписи для каждого раздела;
- Активный элемент подсвечивается и может сопровождаться анимацией или индикатором;
- Часто применяется вместе с
ScreenManagerдля переключения между экранами; - Поддерживает компактный режим (только иконки) и расширенный (иконки + текст).
Образец navigation_rail:

Преимущества:
- Экономит горизонтальное пространство на широких экранах;
- Чёткая визуальная структура — пользователю проще ориентироваться;
- Гибкость — можно использовать только иконки или добавить текст;
- Интеграция с экранами — легко связывать элементы с
ScreenManager.
Где используется:
- Десктопные приложения — панели слева для основных разделов;
- Планшеты и широкие мобильные экраны — альтернативная навигация;
- Панели админки или рабочего пространства — быстрый доступ к разделам;
- Приложения с множеством разделов — помогает структурировать интерфейс вертикально.
Для моих целей больше подходит NavigationRail, так как моё приложение изначально задумывалось для широких экранов, на которых человек обычно работает. Сам я тоже часто использую desktop-приложения с боковым меню.
Как пользователь скажу, что это довольно удобно, когда можно сразу выбрать интересующий раздел. При этом отпадает необходимость в кнопке «Назад».
navigation_panel.kv
Начну с создания файла navigation_panel.kv, который будет служить содержимым панели навигации. Этот файл будет отдельным и может импортироваться в другие kv-файлы, в которых находятся элементы экранов.
#:kivy 2.3.1
<CommonNavigationRailItem@MDBoxLayout>:
icon: ""
text: ""
orientation: "vertical"
spacing: "4dp"
size_hint_y: None
height: "72dp"
MDNavigationRailItemIcon:
icon: root.icon
MDNavigationRailItemLabel:
text: root.text
<NavigationPanel@MDBoxLayout>:
orientation: "horizontal"
MDNavigationRail:
id: rail
MDNavigationRailFabButton:
icon: "clock-outline"
CommonNavigationRailItem:
icon: "timer-outline"
text: "Таймеры"
CommonNavigationRailItem:
icon: "compass-outline"
text: "Гид"
CommonNavigationRailItem:
icon: "information-outline"
text: "Инфо"
MDScreen:
pos_hint: {"bottom": .98}
y: "12dp"
MDNavigationRailMenuButton:
icon: "menu"
on_release: app.open_menu(self)CommonNavigationRailItem:
- Создаётся переиспользуемый (template) layout для пункта меню:
- Расширяет:
MDBoxLayout; - Параметры:
icon,text(передаются как свойства);orientation: "vertical"— элементы размещаются друг над другом;spacing: "4dp"— расстояние между иконкой и подписью;size_hint_y: None,height: "72dp"— фиксированная высота.
- Расширяет:
- Содержимое:
MDNavigationRailItemIcon(иконка);MDNavigationRailItemLabel(текст).
- Зачем нужно: позволяет легко создавать однотипные пункты меню с иконкой и подписью.
NavigationPanel:
- Это повторно используемый виджет — контейнер горизонтального меню с боковой навигацией:
- Расширяет:
MDBoxLayout; orientation: "horizontal"— навигация будет слева, остальное — справа;- Внутри только один child — сам
MDNavigationRail.
- Расширяет:
MDNavigationRail:
- MDNavigationRailFabButton — большая "плавающая" кнопка для акцента (например, по центру или в топе меню);
- CommonNavigationRailItem (x3) — пункты меню с разными иконками и подписями («Таймеры», «Гид», «Инфо»);
- MDScreen (снизу меню) — отдельная зона, в которой размещается кнопка-гамбургер (
MDNavigationRailMenuButton):pos_hint: {"bottom": .98}иy: "12dp"— закрепляет кнопку у нижней границы рейла, чуть приподнимая;on_release: app.open_menu(self)— по клику вызывает функцию открытия меню в главном App-классе;- Теперь меню-гамбургер находится в
navigation_panel.kv, а не вtimers_screen.kvкак это было изначально.
timers_screen.kv
Мне нужно встроить панель навигации в экран «Таймеры».
#:kivy 2.3.1
#:include kv/navigation_panel.kv
# Кастомная панель для каждого таймера
<ExpansionPanelItem>:
MDExpansionPanelHeader:
MDListItem:
theme_bg_color: "Custom"
md_bg_color: self.theme_cls.surfaceContainerLowColor
ripple_effect: False
MDListItemSupportingText:
text: root.title
TrailingPressedIconButton:
id: chevron
icon: "chevron-right"
on_release: app.tap_expansion_chevron(root, chevron)
MDExpansionPanelContent:
orientation: "vertical"
padding: "12dp", 0, "12dp", "12dp"
md_bg_color: self.theme_cls.surfaceContainerLowColor
adaptive_height: True
MDLabel:
text: "Информация о таймере"
adaptive_height: True
padding: "16dp", "12dp"
MDListItem:
theme_bg_color: "Custom"
md_bg_color: self.theme_cls.surfaceContainerLowColor
ripple_effect: False
on_release: None
MDListItemLeadingIcon:
icon: "information-outline"
MDListItemHeadlineText:
text: root.info
MDBoxLayout:
padding: [8, 8, 0, 0]
spacing: 8
size_hint_y: None
height: self.minimum_height
MDButton:
timer_id: root.timer_id
style: "outlined"
size_hint_x: 1
on_release: app.root.get_screen("timers_screen").switch_timer(self)
MDButtonText:
text: "Открыть"
MDButton:
timer_id: root.timer_id
style: "outlined"
size_hint_x: 1
MDButtonText:
text: "Редактировать"
MDButton:
timer_id: root.timer_id
style: "outlined"
size_hint_x: 1
MDButtonText:
text: "Удалить"
# Основной экран
<TimersScreen>:
NavigationPanel:
MDScreen:
md_bg_color: self.theme_cls.secondaryContainerColor
ScrollView:
size_hint_x: .9
pos_hint: {"center_x": .5, "center_y": .5}
do_scroll_x: False
do_scroll_y: True
MDList:
id: container
spacing: "8dp"
padding: [56, 8, 56, 0] # [слева, сверху, справа, снизу]Разбор новых строк в kv-файле:
#:include kv/navigation_panel.kv:- Это директива в начале kv-файла, позволяющая подключить другой kv-файл (в данном случае,
navigation_panel.kv); - Все определения, виджеты и шаблоны из указанного файла становятся доступны — удобно для логического разделения кода и переиспользования компонентов.
- Это директива в начале kv-файла, позволяющая подключить другой kv-файл (в данном случае,
оn_release: app.root.get_screen("timers_screen").switch_timer(self):- Переключает на выбранный экран таймера.
NavigationPanel:- Это переиспользуемый контейнер для боковой навигации, объявленный ранее в другом kv-файле. Здесь он вставляется в корень экрана (
TimersScreen):- обеспечивает навигационное меню (панель слева);
- вся остальная часть экрана размещается справа.
- Это переиспользуемый контейнер для боковой навигации, объявленный ранее в другом kv-файле. Здесь он вставляется в корень экрана (
md_bg_color: self.theme_cls.secondaryContainerColor:- Эта строка задаёт цвет фона для MDScreen по теме приложения:
- Механизм KivyMD
theme_clsпозволяет использовать разные варианты брендированных цветов (primary, secondary, background и т.д.); secondaryContainerColor— один из предустановленных оттенков, подходящий для подложки/контейнера.
- Механизм KivyMD
- Эта строка задаёт цвет фона для MDScreen по теме приложения:
main.py
Теперь нужно создать класс CommonNavigationRailItem, который хранит данные и обеспечивает правильное размещение пунктов меню на панели с нужными отступами.
Новые импорты:
from kivy.properties import StringProperty
from kivymd.uix.navigationrail import MDNavigationRailItemРазбор импортов:
from kivy.properties import StringProperty— импортирует свойство строки, которое используется в Kivy для автоматического обновления интерфейса при изменении значения переменной. Благодаря этому можно привязывать данные к виджетам (например, менять текст или иконку в интерфейсе динамически);from kivymd.uix.navigationrail import MDNavigationRailItem— импортирует базовый элемент боковой навигации (MDNavigationRailItem) из библиотеки KivyMD. Он служит строительным блоком для элементов навигационной панели (например, пунктов меню с иконками и подписями).
Класс CommonNavigationRailItem:
class CommonNavigationRailItem(MDNavigationRailItem):
"""Класс панель навигации"""
text = StringProperty()
icon = StringProperty()Разбор кода:
class CommonNavigationRailItem(MDNavigationRailItem):— создаётся новый класс, который наследуется отMDNavigationRailItem. Это значит, что он получает все свойства и методы базового класса, но может быть дополнен своими параметрами;text = StringProperty()— объявляет свойствоtext, которое будет хранить подпись элемента навигации. Это свойство можно использовать в.kv-файле, например, для отображения текста рядом с иконкой;icon = StringProperty()— объявляет свойствоicon, которое содержит имя иконки из набора Material Design Icons. Изменение этого свойства автоматически обновит иконку на экране.
Запуск с новым дизайном
Настало время посмотреть, как выглядит дизайн боковой панели navigation_rail.
Запускаю приложение в terminal:
poetry run kawai-focusРаскрою «Таймер 1» и нажму на кнопку «Таймеры» с изображением секундомера:

Теперь видны новые кнопки «Редактировать» и «Удалить», которые пока не подключены. Слева на панели подсвечена кнопка и надпись «Таймеры». При подключении кнопок она будет подсвечена по умолчанию, так как экран «Таймеры» — первый, который пользователь видит при запуске приложения.

Закрою «Таймер 1», нажму на кнопку с иконкой компаса и на кнопку бургер-меню:

Кнопка «Гид» предназначена для будущего экрана, отвечающего за обучение пользователя работе с программой и техникой Pomodoro. Я хочу, чтобы моя программа была чем-то большим, чем просто набором таймеров. Для меня важно, чтобы каждый таймер приносил максимальную пользу пользователю.
Нажму на кнопку с иконкой i и раскрою все таймеры просто для смены картинки на экране:

Кнопка «Инфо» предназначена для будущего экрана с информацией о программе: название, версия, репозиторий и, возможно, что-то ещё.
Чуть не забыл упомянуть про квадратную кнопку с круглыми углами в верхней части панели, на которой изображены часы. Насколько я понял, это кнопка «Домой», но я планирую сделать там большую иконку приложения. Видел похожее решение в одном приложении на Flet, где иконка была размещена именно так, и выглядело это очень красиво.
Анонс на следующие статьи
Сегодня я встроил панель NavigationRail и добавил несколько кнопок в выпадающий элемент таймера на экране «Таймеры». А что насчёт экрана «Таймер»? Он был анонсирован в предыдущей статье, но работа над ним пока ещё не завершена.
Переход с дизайна Kivy на Material Design с библиотекой KivyMD оказался не таким простым и быстрым, как я ожидал. Различие в названиях классов для виджетов — не единственная сложность. Например, старый способ работы с мелодией таймера теперь почему‑то не даёт программе запускаться, хотя раньше всё работало нормально. Похоже, придётся кардинально менять логику класса TimerScreen, который управляет таймером.
Моя следующая статья выйдет 13.11.2025. В ней я продолжу работу над изменением дизайна, а вместе с ним и механики приложения. Очень надеюсь, что экран «Таймер» получится красивым и хорошо работающим.
Если у вас есть мысли о том, как можно улучшить проект, пишите в комментариях — с удовольствием ознакомлюсь с вашими предложениями!
Читайте продолжение — не пропустите!
Заключение
- Подключена боковая панель
NavigationRail; - Добавлены новые кнопки на экран «Таймеры».
Ссылки к статье
- Мои статьи Arduinum628 на Код на салфетке;
- Репозиторий проекта Kawai.Focus.
Комментарии
Оставить комментарийВойдите, чтобы оставить комментарий.
Комментариев пока нет.