Перейти к контенту

Kawai.Focus - приложение для фокусировки внимания (часть 12)

Kawai.Focus Eugene Kaddo 64

Данная статья посвящена:

  • Фреймворку Kivy в проекте Kawai.Focus;
  • Material Design для Kivy на библиотеке KivyMD 2.0.0;
  • Внедрению панели навигации;
  • Добавлению новых кнопок на экран  «Таймеры».

Kawai.Focus - приложение для фокусировки внимания (часть 12)
Kawai.Focus Eugene Kaddo 64

Вступление

Всем доброго дня! В предыдущей статье 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:

Преимущества:

  1. Экономит горизонтальное пространство на широких экранах;
  2. Чёткая визуальная структура — пользователю проще ориентироваться;
  3. Гибкость — можно использовать только иконки или добавить текст;
  4. Интеграция с экранами — легко связывать элементы с ScreenManager.

Где используется:

  1. Десктопные приложения — панели слева для основных разделов;
  2. Планшеты и широкие мобильные экраны — альтернативная навигация;
  3. Панели админки или рабочего пространства — быстрый доступ к разделам;
  4. Приложения с множеством разделов — помогает структурировать интерфейс вертикально.

Для моих целей больше подходит NavigationRail, так как моё приложение изначально задумывалось для широких экранов, на которых человек обычно работает. Сам я тоже часто использую desktop-приложения с боковым меню.

Как пользователь скажу, что это довольно удобно, когда можно сразу выбрать интересующий раздел. При этом отпадает необходимость в кнопке «Назад».

Начну с создания файла 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;
    • Параметры:
      • icontext (передаются как свойства);
      • orientation: "vertical" — элементы размещаются друг над другом;
      • spacing: "4dp" — расстояние между иконкой и подписью;
      • size_hint_y: Noneheight: "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);
    • Все определения, виджеты и шаблоны из указанного файла становятся доступны — удобно для логического разделения кода и переиспользования компонентов.
  • оn_release: app.root.get_screen("timers_screen").switch_timer(self):
    • Переключает на выбранный экран таймера.
  • NavigationPanel:
    • Это переиспользуемый контейнер для боковой навигации, объявленный ранее в другом kv-файле. Здесь он вставляется в корень экрана (TimersScreen):
      • обеспечивает навигационное меню (панель слева);
      • вся остальная часть экрана размещается справа.
  • md_bg_color: self.theme_cls.secondaryContainerColor:
    • Эта строка задаёт цвет фона для MDScreen по теме приложения:
      • Механизм KivyMD theme_cls позволяет использовать разные варианты брендированных цветов (primary, secondary, background и т.д.);
      • secondaryContainerColor — один из предустановленных оттенков, подходящий для подложки/контейнера.

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

Если у вас есть мысли о том, как можно улучшить проект, пишите в комментариях — с удовольствием ознакомлюсь с вашими предложениями!

Читайте продолжение — не пропустите!


Заключение

  1. Подключена боковая панель NavigationRail;
  2. Добавлены новые кнопки на экран «Таймеры».

Ссылки к статье

Аватар автора

Автор

Eugene Kaddo

Программист фрилансер. Пишу боты, парсеры и скрипты на Python3. По вопросам фриланс заказа программы пишите на почту, указанную в профиле. Автор статей по программированию. Увлекаюсь lego, робототехникой на arduino, рок музыкой, программированием на Python3 и C.

Войдите, чтобы оставить комментарий.

Комментариев пока нет.