Kivy — фреймворк для кроссплатформенной разработки №1

aulf2nv4os3bphs-zjggqlfycuo.png


В мире кроссплатформенной разработки под мобильные платформы сейчас, наверное, как это не прискорбно, доминируют два фреймворка — Xamarin и React Native. Xamarin — потому что является «приемным сыном» компании Microsoft и, гордо размахивая костылями, активно пиарится последней, а React Native — отпрыск не менее известной Facebook, который с не меньшей долей гордости отращивает бороды уставшим на нем разрабатывать программистам. Для себя я уже давно нашел альтернативу, а тех, кто еще не знаком с фреймворком для кроссплатформенной разработки Kivy, добро пожаловать под кат…
Чем хорош Kivy? Во-первых, тем, что это не JavaScript. Это Python. Отсюда скорость разработки, лаконичность кода, возможность моментально изменять и отслеживать изменения в приложениях, это возможность просто писать код в то время, когда другие отращивают бороды в безуспешных попытках окончить свое приложение или мастерят очередные сверхмодные костыли для своих проектов. Во-вторых, это на 99.9% настоящаий кроссплатформенный фрейворк, с которым вы можете быть уверенными, что ваш код, единожды написанный, запустится у будет работать на всех доступных платформах. Для справки: Xamarin — это всего лишь 60% кода, который можно переиспользовать, несмотря на заявления разработчиков о 80%. Kivy зрелый фреймворк, который разрабатывается с 2011 года, старше свого собрата React Native (2015 год) и одногодка Xamarin (2011 год).

Для сегодняшней статьи я подготовил небольшой пример, который наглядно демонстрирует все вышеперечисленные преимущества Kivy. Мы создадим новый проект с помощью консольной утилиты CreatorKivyProject, посмотрим, как анимировать виджеты в Kivy и построим один экран следующего содержания:

3pzi4alja54mws9iwkghah_tgko.gif

Итак, скачайте утилиту CreatorKivyProjectи создайте новый проект, следуя инструкциям в README. После выполнения простой команды будет создан пустой проект с одним главным экраном и двумя дополнительными экранами 'О программе' и 'Лицензия', которые можно открыть в меню Navigation Drawer. Это ещё не готовое приложение для мобильных платформ, но его уже можно запускать и тестировать прямо из исходных текстов на вашем компьютере. Для запуска проекта вам необходимо выполнить сценарий main.py, который является точкой входа в приложение.

h2urhychapurbohx8wuhcvyz2yc.png

После запуска будут доступны следующие экраны:

sfxcmepcerk7t-ebsu1gvlnwviq.png

Наша задача интегрировать в экран стопку из четырех кнопок типа FloatingActionButton, плавающие подписи к ним и минимальный функционал на события кнопок. Под стопкой кнопок я подразумеваю кнопки, которые размещаются в правом нижнем углу экрана и накладываются друг на друга. Поскольку такая стопка может пригодится не в одном проекте, сделаем модуль floatingactionsbuttons.py, который в последствии сможем использовать везде, в любом проекте и на любой платформе. Откроем директорию libs/applibs, в которой находятся пакеты и модули приложения и создадим пакет floatingactionsbuttons c файлами __init__.py, floatingactionsbuttons.kv и floatingactionsbuttons.py:

briixnugbuab5iykd1zfh230oic.png

Файл floatingactionsbuttons.kv описывает разметку UI элементов на специальном языке Kv Language, который намного проще и понятней, чем XAML в Xamarin, xml в Java или JSX-разметке в React Native.
Файл floatingactionsbuttons.py управляет поведением элементов и их логикой, которые описаны в floatingactionsbuttons.kv.

Вот так четко и структурировано с легко просматриваемой иерархией элементов выглядит разметка стопки с нашими кнопками:

floatingactionsbuttons.kv
#: import Window kivy.core.window.Window
#: import MDFloatingActionButton kivymd.button.MDFloatingActionButton

:
    x: Window.width - (self.width + dp(21))
    y: dp(25)
    size_hint: None, None
    size: dp(46), dp(46)
    elevation: 5
    md_bg_color: app.theme_cls.primary_color
    on_release: self.parent.callback(self)
 
:
    size_hint: None, None
    height: dp(20)
    width: label.texture_size[0]
    border_color_a: .5
    md_bg_color: app.theme_cls.primary_color
    x: -self.width

    Label:
        id: label
        color: 0, 0, 0, 1
        bold: True
        markup: True
        text: '  %s  ' % root.text

:
    FloatingButton:
        id: f_btn_1
        icon: list(root.floating_data.values())[0]
    FloatingButton:
        id: f_btn_2
        icon: list(root.floating_data.values())[1]
    FloatingButton:
        id: f_btn_3
        icon: list(root.floating_data.values())[2]

    FloatingLabel:
        id: f_lbl_1
        text: list(root.floating_data.keys())[0]
        y: dp(117)
    FloatingLabel:
        id: f_lbl_2
        text: list(root.floating_data.keys())[1]
        y: dp(170)
    FloatingLabel:
        id: f_lbl_3
        text: list(root.floating_data.keys())[2]
        y: dp(226)

    MDFloatingActionButton:
        icon: root.icon
        size: dp(56), dp(56)
        x: Window.width - (self.width + dp(15))
        md_bg_color: app.theme_cls.primary_color
        y: dp(15)
        on_release: root.show_floating_buttons()


Давайте наглядно посмотрим, каким элементам в нашей стопке соответствует разметка. Мы создали подпись, которая будет соответствовать каждой кнопке:

4odzyazlgkkt1bkq4w1m3tsp_2g.png

кнопку, которая будет находится в стопке:

9olwcgs-blldt5_k6eze7rxyjb0.png

главную кнопку, которая анкерится в нижнем правом углу экрана:

dub5snd8crfyzewnfkact_2ix-c.png

и разместили все это на экране, указав подписям позиции в левой части экрана (за пределами видимости), а всем кнопкам — позиции в правом нижнем углу:

euovpkcnqupwlrqotpyitmnth-w.png

Кнопки у нас просто накладываются друг на друга. Теперь самое время их оживить. Откроем файл

floatingactionsbuttons.py
import os

from kivy.animation import Animation
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.logger import PY2
from kivy.lang import Builder
from kivy.properties import StringProperty, DictProperty, ObjectProperty
from kivy.metrics import dp

from kivymd.card import MDCard


kv_file = os.path.splitext(__file__)[0] + '.kv'
if PY2:
    Builder.load_file(kv_file)
else:
    with open(kv_file, encoding='utf-8') as kv:
        Builder.load_string(kv.read())


class FloatingLabel(MDCard):
    text = StringProperty()


class FloatingActionButtons(FloatLayout):
    icon = StringProperty('checkbox-blank-circle')
    callback = ObjectProperty(lambda x: None)
    floating_data = DictProperty()
    show = False

    def __init__(self, **kwargs):
        super(FloatingActionButtons, self).__init__(**kwargs)

        self.lbl_list = [self.ids.f_lbl_1, self.ids.f_lbl_2, self.ids.f_lbl_3]
        self.btn_list = [self.ids.f_btn_1, self.ids.f_btn_2, self.ids.f_btn_3]

    def show_floating_buttons(self):
        step = dp(46)
        for btn in self.btn_list:
            step += dp(56)
            Animation(y=step, d=.5, t='out_elastic').start(btn)

        self.show = True if not self.show else False
        self.show_floating_labels() if self.show \
            else self.hide_floating_labels()

    def show_floating_labels(self):
        i = 0
        for lbl in self.lbl_list:
            i += .5
            pos_x = Window.width - (lbl.width + dp(46 + 21 * 1.5))
            Animation(x=pos_x, d=i, t='out_elastic').start(lbl)

    def hide_floating_buttons(self):
        for btn in self.btn_list:
            Animation(y=25, d=.5, t='in_elastic').start(btn)

    def hide_floating_labels(self):
        i = 1
        for lbl in self.lbl_list:
            i -= .3
            Animation(x=-lbl.width, d=i, t='out_elastic').start(lbl)
        self.hide_floating_buttons()


В Kivy создание анимаций происходит при помощи класса Animation. Как и в Java достаточно указать параметр элемента, который мы хотим анимировать, передав конечное значение. Например, обратите внимание, что высота наших кнопки в kv разметке установлена в значение 25, то есть 25 пикселей от нижней части экрана:

:
    x: Window.width - (self.width + dp(21))
    y: dp(25)  # позиция кнопки по оси y


Поскольку у нас три кнопки, в цикле мы указываем каждой высоту (параметр «y»), на которую ее необходимо поднять, и передаем имя функции («out_elastic»), которая отвечает за тип анимации (в нашем проекте я использовал анимацию эластичной пружины), а все остальное делается автоматически:

    def show_floating_buttons(self):
        '''Вызывается при тапе на кнопку в правом нижнем углу экрана.
        Выполняет анимацию трех кнопок Puthon/C++/Php. '''

        step = dp(46)
        for btn in self.btn_list:
            step += dp(56)
            Animation(y=step, d=.5, t='out_elastic').start(btn)


Анимирование подписей точно такое же, за исключением того, что в Animation мы передаем значение параметру x — позиция элемента по горизонтали:

    def show_floating_labels(self):
        '''Вызывается при тапе на кнопку в правом нижнем углу экрана.
        Выполняет анимацию трех подписей Puthon/C++/Php. '''

        i = 0  # скорость выполнение анимации в милисекундах
        for lbl in self.lbl_list:
            i += .5
            pos_x = Window.width - (lbl.width + dp(46 + 21 * 1.5))
            Animation(x=pos_x, d=i, t='out_elastic').start(lbl)

Пакет готов. Как его добавить в наш единственный экран? Откройте управляющий главным экраном файл AnimatedButtons/libs/uix/baseclass/basescreen.py:

snjhcffdytwf9dagd7hi0fjctpk.png

… импортируйте созданный нами пакет и добавьте его в экран:

from libs.applibs.floatingactionbuttons import FloatingActionButtons


class BaseScreen(Screen):
    '''Главный экран приложения.'''

    def on_enter(self):
        '''Вызывается в момент открытия экрана.'''

        # Добавляем в экран стопку анимарованных кнопок.
        self.add_widget(FloatingActionButtons(
            icon='lead-pencil',
            floating_data={
                'Python': 'language-python',
                'Php': 'language-php',
                'C++': 'language-cpp'},
            callback=self.set_my_language))

Как видите, все просто, прозрачно и быстро. Боюсь представить, как реализация подобного функционала, учитывая, сколько кода потребовалось автору, чтобы реализовать приложение, типа «Hello World» в этой статье, будет выглядеть на React Native. Рядом с Kivy я могу поставить лишь Xamarin, который при сборке пакетов тянет за собой Mono и другие библиотеки, так же, как Kivy тянет за собой интерпретатор Python. Готовые приложения на Kivy и Xamarin имеют одинаковый размер и примерную скорость запуска, но у Xamarin в данный момент гораздо больше проблем, поэтому я с уверенностью могу сказать, что на сегодняшний день Kivy — фреймворк для кроссплатформенной разработки №1!

Исходный код проекта вы можете скачать на GitHub.

© Habrahabr.ru