Python GUI. Библиотека KivyMD. Использование Blur эффекта

от автора

Приветствую вас, дорогие любители и знатоки Python! Сегодня как всегда будем делать обзор фреймворка для кроссплатформенной разработки Kivy и библиотеки виджетов в стиле Google Material Design для фреймворка KivyKivyMD. В этой статье мы рассмотрим как сделать blur эффект отдельных компонентов интерфейса пользователя. Я уже делал похожий обзор в статье Материальный Python. Кастомные карточки с OpenGL-эффектами, но нижеследующий материал несколько сложнее и в прямом смысле динамичнее. Что ж, начнем…

А для тех, кто незнаком ни с Kivy ни с библиотекой KivyMD, вкратце напомню:

Kivy — кроссплатформенный фреймворк с открытым исходным кодом, написанный с использованием Python/Cython. Поддерживает устройства ввода: WM_Touch, WM_Pen, Mac OS X Trackpad Magic Mouse, Mtdev, Linux Kernel HID, TUIO

KivyMD — сторонняя open source библиотека, написанная на чистом Python, реализующая набор виджетов в стиле Google Material Design, для использования с фреймворком Kivy. Вне экосистемы Kivy библиотека не используется. Текущее состояние — бета. Мастер версия на GitHub — 1.0.0

Два этих проекта развиваются отдельно друг от друга, но поскольку из коробки Kivy имеет стандартный «убогий» дизайн виджетов, а современные приложения не приемлют серости и всем хочется юзать красивые красивости, люди часто путают Kivy и KivyMD, задавая, например, вопросы типа «Как мне скомпилировать для моего приложения KivyMD вместо Kivy?«, что, конечно же, некорректно, поскольку KivyMD это всего лишь дополнительные виджеты для фреймворка Kivy, реализованные с помощью него же. Эти виджеты никогда не будут включены в стандартную библиотеку Kivy и не заменят собой страшные дефолтные виджеты Kivy, потому что Kivy хочет быть кроссплатформенным на 100% и не привязывается ни к материальному дизайну ни к какому-либо еще, оставляя все на стороне пользователя: хочется дизайн как у Mac OS — делайте как у Mac OS, хочется дизайн как в Windows — делайте как в Windows, благо все виджеты Kivy кастомизируются легко и быстро.

Ну а наша сегодняшняя задача — сделать нижеследующий концепт экрана:

Основная цель — blur эффект для двух текстовых полей. Анимированный фон я сделал для того, чтобы сделать акцент на динамике. Итак, для работы нам понадобится Python 3.6 — 3.9 (3.10 пока в разработке у Kivy), фреймворк Kivy и библиотека KivyMD. Установим все это дело:

pip install "kivy[base] @ https://github.com/kivy/kivy/archive/master.zip" pip install https://github.com/kivymd/KivyMD/archive/master.zip

Первая команда установит фреймворк Kivy мастер версии, а вторая — библиотеку KivyMD также мастер версии. Все. Можем работать. Откроем консоль и создадим шаблон проекта с помощью инструментов KivyMD:

python3.9 -m kivymd.tools.patterns.create_project \     MVC \     /Users/macbookair \     BlurConcept \     python3.9 \     master \     --name_screen BlurConceptScreen

Будет создан проект с шаблоном MVC в директории /Users/macbookair с именем BlurConcept, виртуальным окружением и всеми необходимыми зависимостями:

Базовый шаблон проекта KivyMD
Базовый шаблон проекта KivyMD

Детально об архитектуре проекта, которую предлагает использовать KivyMD вы можете прочитать в статье Python GUI. Библиотека KivyMD. Шаблон MVC, parallax эффект и анимация контента слайдов

Открываем проект, и удаляем, кроме имя правила, все содержимое файла blur_concept_screen.kv — это разметка нашего единственного экрана в приложении:

blur_concept_screen.kv
blur_concept_screen.kv

Посмотрим на готовый экран приложения и прикинем какие элементы UI мы можем переиспользовать:

Я выделил метки и два текстовых поля. В проекте в каждой директории представлений для каждого экрана (View) есть пакет components:

components для экрана BlurConceptScreenView
components для экрана BlurConceptScreenView

Создадим два пакета в директории components для экрана BlurConceptScreenView — commonlabel и fillfield — метку и текстовое поле с общими параметрами:

commonlabel.py:

from kivymd.uix.label import MDLabel   class CommonLabel(MDLabel):     """It is just a base class for a label with common parameters."""      def __init__(self, **kwargs):         super().__init__(**kwargs)         self.color = (1, 1, 1, 1)         self.font_style = "H4"         self.font_name = "assets/fonts/PlayfairDisplay-Black.otf"         self.adaptive_height = True         self.markup = True

fillfield.py:

from kivymd.uix.textfield import MDTextField   class FillField(MDTextField):     """It is just a base class for a text field with common parameters."""      def __init__(self, **kwargs):         super().__init__(**kwargs)         self.size_hint_x = None         self.width = "320dp"         self.mode = "fill"         self.fill_color_normal = (1, 1, 1, 0.1)         self.fill_color_focus = (1, 1, 1, 0.3)

В файле __init__.py сделаем импорт этих классов, чтобы в дальше их можно было импортировать непосредственно из пакета components:

components/__init__.py:

from .fillfield.fill_field import FillField from .commonlabel.common_label import CommonLabel

Теперь откроем в файл с классом представления экрана:

blur_concept_screen.py
blur_concept_screen.py

… и объявим необходимые константы и методы:

from kivy.animation import Animation from kivy.metrics import dp from kivy.properties import NumericProperty  from View.base_screen import BaseScreenView from View.BlurConceptScreen.components import FillField, CommonLabel   class BlurConceptScreenView(BaseScreenView):     """Implements the login start screen in the user application."""      OPACITY = NumericProperty(0)     SHIFT_Y = NumericProperty(dp(0))     FIELD_WIDTH = NumericProperty(dp(320))     FIELD_HEIGHT = NumericProperty(dp(52))     PADDING = NumericProperty(dp(24))      def on_enter(self, *args):         """         Event called when the screen is displayed: the entering animation is         complete.         """          animation = Animation(SHIFT_Y=dp(140), d=2, t="in_out_quart")         animation.bind(on_complete=self.animation_bg_zoom)         animation.start(self)         Animation(OPACITY=1, d=3).start(self)      def animation_bg_zoom(self, *args):         Animation(height=self.ids.bg.height + self.SHIFT_Y, d=2, t="in_out_quart").start(             self.ids.bg         )

Алгоритм прост: в момент появления экрана в методе on_enter с помощью класса Animation мы будет анимировать два поля класса BlurConceptScreen: OPACITY и SHIFT_Y — прозрачность меток экрана и сдвиг меток по оси Y. Ну, а изменения свойств базового класса в одноименном правиле разметки в KV файле применяются автоматически:

blur_concept_screen.kv:

#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT   <BlurConceptScreenView>      FitImage:         id: bg         size_hint_y: None         source: "assets/images/bg.jpg"         height: root.height + root.SHIFT_Y      CommonLabel:         x: field_login.x         y: root.height - root.SHIFT_Y         opacity: root.OPACITY         text: "You are\nUp Above the Sky"      CommonLabel:         x: field_login.x         opacity: root.OPACITY         font_name: "assets/fonts/PlayfairDisplay-Regular.otf"         text: "Get Started Here"         y: (field_login.y + root.SHIFT_Y) - (root.PADDING + self.height)      FillField:         id: field_login         opacity: root.OPACITY         hint_text: "Login"         icon_left: "account"         pos:             root.center[0] - root.FIELD_WIDTH / 2, \             root.center[1] + root.SHIFT_Y / 4      FillField:         id: field_password         opacity: root.OPACITY         hint_text: "Password"         icon_left: "lock"         pos:             root.center[0] - root.FIELD_WIDTH / 2, \             root.center[1] - root.SHIFT_Y / 4      CommonLabel:         id: lbl_forgot_password         opacity: root.OPACITY         font_style: "Caption"         halign: "center"         y: field_password.y - root.PADDING * 2         text: "Forgot Password?"      MDFillRoundFlatButton:         id: sign_in_button         text: "Sign In"         opacity: root.OPACITY         size_hint_x: None         -width: field_password.width         pos_hint: {"center_x": .5}         font_size: "24sp"         font_name: "assets/fonts/PlayfairDisplay-Black.otf"         md_bg_color: .8, .8, .8, 1         text_color: app.theme_cls.primary_color         ripple_color: app.theme_cls.primary_color[:-1] + [.4]         y: root.SHIFT_Y      CommonLabel:         opacity: root.OPACITY         font_style: "Caption"         font_name: "assets/fonts/PlayfairDisplay-Regular.otf"         halign: "center"         y: dp(160) - root.SHIFT_Y         text:             "Don`t have an Account? " \             "[font=assets/fonts/PlayfairDisplay-Black.otf]Sign Up[/font]"

Уже можем запустить наш проект и увидеть следующую картину:

Как видим, blur эффекта нет. Для добавления эффекта воспользуемся виджетом EffectWidget:

#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT #:import HorizontalBlurEffect kivy.uix.effectwidget.HorizontalBlurEffect #:import VerticalBlurEffect kivy.uix.effectwidget.VerticalBlurEffect #:import effect kivy.uix.effectwidget.EffectWidget   <BlurConceptScreenView>      FitImage:      CommonLabel:      CommonLabel:      EffectWidget:         opacity: root.OPACITY         effects:             ( \             HorizontalBlurEffect(size=12), \             VerticalBlurEffect(size=12), \             )          canvas.before:             StencilPush             RoundedRectangle:                 radius: [10, 10, 0, 0]                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] + root.SHIFT_Y / 4             RoundedRectangle:                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 radius: [10, 10, 0, 0]                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] - root.SHIFT_Y / 4             StencilUse          canvas.after:             StencilUnUse             RoundedRectangle:                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 radius: [10, 10, 0, 0]                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] + root.SHIFT_Y / 4             RoundedRectangle:                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 radius: [10, 10, 0, 0]                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] - root.SHIFT_Y / 4             StencilPop          FitImage:             id: bg_blur             size_hint_y: None             source: "assets/images/bg.jpg"             height: root.height + root.SHIFT_Y      FillField:      FillField:      CommonLabel:      MDFillRoundFlatButton:      CommonLabel:

EffectWidget способен применять различные графические эффекты к своим потомкам с помощью пользовательских шейдеров и OpenGL. Что мы сделали в нашем конкретном случае, чтобы получить blur эффект? Мы воспользовались инструкциями холста (canvas), определили для них размер и позицию равные размеру и позиции двум текстовым полям, с помощью трафаретов (StencilPush/StencilUse) выделили эти области на холсте и применили blur эффект в свойствах EffectWidget к фоновому изображению:

    EffectWidget:         effects:             ( \             HorizontalBlurEffect(size=12), \             VerticalBlurEffect(size=12), \             )          canvas.before:             ...          canvas.after:             ...          FitImage:             id: bg_blur             size_hint_y: None             source: "assets/images/bg.jpg"             height: root.height + root.SHIFT_Y

Теперь нам нужно синхронизировать анимации фоновых изображений, добавив в метод animation_bg_zoom в классе представления анимацию для изображения bg_blur:

class BlurConceptScreenView(BaseScreenView):     def animation_bg_zoom(self, *args):         Animation(height=self.ids.bg_blur.height + self.SHIFT_Y, d=2, t="in_out_quart").start(             self.ids.bg_blur         )

Если мы в KV разметке в файле blur_concept_screen.kv оставим только EffectWidget с инструкциями холста:

#:import HorizontalBlurEffect kivy.uix.effectwidget.HorizontalBlurEffect #:import VerticalBlurEffect kivy.uix.effectwidget.VerticalBlurEffect #:import effect kivy.uix.effectwidget.EffectWidget   <BlurConceptScreenView>      EffectWidget:         opacity: root.OPACITY         effects:             ( \             HorizontalBlurEffect(size=12), \             VerticalBlurEffect(size=12), \             )          canvas.before:             StencilPush             RoundedRectangle:                 radius: [10, 10, 0, 0]                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] + root.SHIFT_Y / 4             RoundedRectangle:                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 radius: [10, 10, 0, 0]                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] - root.SHIFT_Y / 4             StencilUse          canvas.after:             StencilUnUse             RoundedRectangle:                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 radius: [10, 10, 0, 0]                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] + root.SHIFT_Y / 4             RoundedRectangle:                 size: root.FIELD_WIDTH, root.FIELD_HEIGHT                 radius: [10, 10, 0, 0]                 pos:                     root.center[0] - root.FIELD_WIDTH / 2, \                     root.center[1] - root.SHIFT_Y / 4             StencilPop          FitImage:             id: bg_blur             size_hint_y: None             source: "assets/images/bg.jpg"             height: root.height + root.SHIFT_Y

… и запустим проект, то сможем наглядно увидеть как это работает:

Далее мы просто добавляем все остальные виджеты на экран. К сожалению, я не смог добавить gif изображения, которые пошагово демонстрируют добавление всех элементов, потому что новый редактор Хабра просто наотрез отказался грузить какие-либо изображения после последней гифки в статье. Обидно, но ладно. Всем спасибо за внимание и до новых встреч!


ссылка на оригинал статьи https://habr.com/ru/articles/586798/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *