Kivy — еще проще, еще нативнее

a25615e3803a4706a71cd902b82f269e.png

Продолжаем серию статей о разработке мобильных приложений с фреймворком Kivy. Сегодня речь пойдет о замечательной библиотеке KivyMD — библиотеке для построения нативного интерфейса в стиле Android Material Design, написанной с использованием и для фреймворка Kivy. Откровенно говоря, лично я бесконечно рад, что отпала необходимость лепить и созерцать кривые, темные и страшные кастомные виджеты в Kivy приложениях. Используя в своих проектах библиотеку KivyMD плюс немного фантазии, вряд ли кто-то сможет визуально отличить, написана ли ваша программа на Java или с использованием фрейворка Kivy и Python.


Скачайте, распакуйте KivyMD, зайдите в корневой каталог распакованного архива и выполните установку:


python setup.py install

Далее, установите зависимости для KivyMD:


pip install kivy-garden
garden install recycleview

После установки библиотеки вы можете запустить тестовый пример из скачанного вами и распакованного архива:


python kitchen_sink.py

После запуска вы увидите приложение, демонстрирующее нативные виджеты и контроллы, которые доступны вам для использования в ваших проектах:



В статье мы не будем останавливаться на каких-то конкретных виджетах библиотеки (их создание и параметры прекрасно описаны в том же самом kitchen_sink.py), а создадим простое демонстрационное приложение «Контакты» с использованием KivyMD. Наше приложение будет уметь создавать контакты и группы, а также добавлять в них созданные контакты. Ну, и попутно более детально осветим некоторые аспекты создания интерфейса приложений в Kivy:



Для простого создания дефолтного проекта на Kivy рекомендую CreatorKivyProject, детальное описание работы с которым описанно в этой статье. Итак, следуя инструкциям в статье по ссылке, проект DemoKivyContacts создан. Откроем файл по пути DemoKivyContacts/libs/uix/kv/startscreen.kv, безжалостно удалим все его содержимое и «нарисуем» стартовый экран своего приложения!


bd61139156fa42b0833f2a023c055e29.png

Вот так выглядит разметка данного интерфейса в Kivy-Language:


startscreen.kv
#:kivy 1.9.1 
#:import CreateContact libs.uix.createcontact.CreateContact 
#:import CallContact libs.uix.callcontact.CallContact 
#:import EmptyScreen libs.uix.emptyscreen.EmptyScreen 
#:import Toolbar kivymd.toolbar.Toolbar 
#:import MDTabbedPanel kivymd.tabs.MDTabbedPanel 
#:import MDTab kivymd.tabs.MDTab 

############################################################################### 
# 
#                              СТАРТОВЫЙ ЭКРАН 
# 
############################################################################### 
: 
    id: root.manager 
    Screen: 
        name: 'root_screen' 
        BoxLayout: 
            #canvas: 
            #    Rectangle: 
            #        pos: self.pos 
            #        size: self.size 
            #        source: 'data/images/background.jpg' 
            orientation: 'vertical' 
            #################################################################### 
            # 
            #                            ACTION BAR 
            # 
            #################################################################### 
            Toolbar: 
                #canvas.before: 
                #    Rectangle: 
                #        pos: self.pos 
                #        size: self.size 
                #        source: 'data/images/background_toolbar.jpg' 

                id: action_bar 
                #background_color: app.data.alpha 
                background_color: app.theme_cls.primary_color 
                title: app.data.string_lang_contacts 
                left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]] 
                right_action_items: [['more-vert', lambda x: None]] 
            #################################################################### 
            # 
            #                           TABBED PANEL 
            # 
            #################################################################### 
            MDTabbedPanel: 
                id: tabs 
                tab_display_mode: 'text' 
                #tab_color: app.data.alpha 
                tab_text_color: app.data.tab_text_color 
                tab_indicator_color: app.data.tab_indicator_color 

                MDTab: 
                    name: 'contacts' 
                    text: app.data.string_lang_contacts 
                    on_tab_press: app.on_tab_press(self.name) 
                    ScreenManager: 
                        id: screen_manager_tab_contacts 
                        Screen: 
                            name: 'empty_contacts_list' 
                            EmptyScreen: 
                                image: 'data/images/contacts.png' 
                                text: app.data.string_lang_add_contacts 
                                callback: app.show_form_create_contact 
                                disabled: False 
                        Screen: 
                            name: 'create_contact' 
                            CreateContact: 
                MDTab: 
                    name: 'groups' 
                    text: app.data.string_lang_groups 
                    on_tab_press: app.on_tab_press(self.name) 
                    ScreenManager: 
                        id: screen_manager_tab_groups 
                        Screen: 
                            name: 'empty_groups_list' 
                            EmptyScreen: 
                                image: 'data/images/contacts.png' 
                                text: app.data.string_lang_not_groups 
                                callback: lambda: app.create_group() 
                                disabled: False 
    Screen: 
        name: 'call_contact' 
        CallContact:

Как видите, наш экран использует:


Toolbar
    Toolbar: 
        id: action_bar 
        background_color: app.theme_cls.primary_color 
        title: app.data.string_lang_contacts 
        left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]] 
        right_action_items: [['more-vert', lambda x: None]] 

7ed42aa15c5a450186b05ce9dac94a67.png

и MDTabbedPanel с вкладками MDTab
    MDTabbedPanel: 
        id: tabs 
        tab_display_mode: 'text' 
        tab_text_color: app.data.tab_text_color 
        tab_indicator_color: app.data.tab_indicator_color 

        MDTab: 
            name: 'contacts' 
            text: app.data.string_lang_contacts 
            on_tab_press: app.on_tab_press(self.name) 
            ScreenManager: 
                id: screen_manager_tab_contacts 
                Screen: 
                    name: 'empty_contacts_list' 
                    EmptyScreen: 
                        image: 'data/images/contacts.png' 
                        text: app.data.string_lang_add_contacts 
                        callback: app.show_form_create_contact 
                        disabled: False 
                Screen: 
                    name: 'create_contact' 
                    CreateContact: 
        MDTab: 
            name: 'groups' 
            text: app.data.string_lang_groups 
            on_tab_press: app.on_tab_press(self.name) 
            ScreenManager: 
                id: screen_manager_tab_groups 
                Screen: 
                    name: 'empty_groups_list' 
                    EmptyScreen: 
                        image: 'data/images/contacts.png' 
                        text: app.data.string_lang_not_groups 
                        callback: lambda: app.create_group

54aef545ef884504b69768e1bdff666a.png

Эти виджеты библиотеки KivyMD мы импортировали в самом начале файла разметки startscreen.kv:


#:import Toolbar kivymd.toolbar.Toolbar 
#:import MDTabbedPanel kivymd.tabs.MDTabbedPanel 
#:import MDTab kivymd.tabs.MDTab

Данные инструкции в Kivy-Language аналогичны импорту в python сценариях:


from kivymd.toolbar import Toolbar 
from kivymd.tabs import MDTabbedPanel 
from kivymd.tabs import MDTab

К слову, в kv-файле вы можете включать другие файлы разметки, если интерфейс, например, слишком сложный:


#:include your_kv_file.kv

У нас имеются две вкладки на MDTabbedPanel — «Контакты» и «Группы». Первая («Контакты») будет содержать виджет ScreenManager (менеджер экранов), в котором мы разместим два, говоря языком Java, Activity:


Вкладка «Контакты»
        MDTab: 
            name: 'contacts' 
            text: app.data.string_lang_contacts 
            on_tab_press: app.on_tab_press(self.name) 
            ScreenManager: 
                id: screen_manager_tab_contacts 
                Screen: 
                    name: 'empty_contacts_list' 
                    EmptyScreen: 
                        image: 'data/images/contacts.png' 
                        text: app.data.string_lang_add_contacts 
                        callback: app.show_form_create_contact 
                        disabled: False 
                Screen: 
                    name: 'create_contact' 
                    CreateContact: 

Как вы могли заметить, ScreenManager должен включать один или несколько виджетов Screen (экранов), которые будут содержать наш контент (Activity). В нашем случае это EmptyScreen (пустой экран) и CreateContact (форма создания нового контакта):


b84e1e0da54e44adb5e02507dcaf7079.png

Переключаться между данными Activity мы будем по их именам:


                Screen: 
                    name: 'empty_contacts_list' 

                    …

                Screen: 
                    name: 'create_contact' 

                    …

… используя объект ScreenManager…


            ScreenManager: 
                id: screen_manager_tab_contacts 

… в программном коде по его идентификатору из созданной нами разметки:


428ae1e0fbea4fa1a75d7de0986fa02b.png

… и переключая Activity посредством передачи аттрибуту current имени нового экрана:


self.manager_tab_contacts.current = 'create_contact'

Теперь «нарисуем» наши Activity — EmptyScreen (пустой экран) и CreateContact (форму создания нового контакта). Создадим файлы разметки интерфейса в директории проекта DemoKivyContacts/libs/uix/kv emptyscreen.kv и createcontact.kv и одноименные python сценарии в директории DemoKivyContacts/libs/uix для управления и передачи параметров созданным виджетам EmptyScreen и CreateContact:


emptyscreen.kv
#:kivy 1.9.1 
#:import MDLabel kivymd.label.MDLabel 
#:import MDFloatingActionButton kivymd.button.MDFloatingActionButton 

: 
    id: empty_screen 

    Image: 
        source: root.image 
        pos_hint: {'center_x': .5, 'center_y': .6} 
        opacity: .5 

    MDLabel: 
        id: label 
        font_style: 'Headline' 
        theme_text_color: 'Primary' 
        color: app.data.text_color 
        text: root.text 
        halign: 'center' 

    MDFloatingActionButton: 
        id: float_act_btn 
        icon: 'plus' 
        size_hint: None, None 
        size: dp(56), dp(56) 
        opposite_colors: True
        elevation_normal: 8
        pos_hint: {'center_x': .9, 'center_y': .1} 
        background_color: app.data.floating_button_color 
        background_color_down: app.data.floating_button_down_color 
        disabled: root.disabled 
        on_release: root.callback()

createcontact.kv
#:kivy 1.9.1 
#:import SingleLineTextField kivymd.textfields.SingleLineTextField 
#:import MDIconButton kivymd.button.MDIconButton 
#:import MDFlatButton kivymd.button.MDFlatButton 
#:import MDFloatingActionButton kivymd.button.MDFloatingActionButton 

: 
    orientation: 'vertical' 

    FloatLayout: 
        size_hint: 1, .3 

        Image: 
            id: avatar 
            pos_hint: {'center_y': .5} 
            source: 'data/images/avatar_empty.png' 

        MDFloatingActionButton: 
            icon: 'plus' 
            size_hint: None, None 
            size: dp(56), dp(56) 
            opposite_colors: True 
            elevation_normal: 8 
            pos_hint: {'center_x': .9, 'center_y': .20} 
            background_color: app.data.floating_button_color 
            background_color_down: app.data.floating_button_down_color 
            on_release: app.choice_avatar_contact() 

    BoxLayout: 
        orientation: 'vertical' 
        padding: 5, 5 
        size_hint: 1, .3 

        BoxLayout: 
            MDIconButton: 
                icon: 'account' 
                disabled: True 
            SingleLineTextField: 
                id: name_field 
                hint_text: 'ИФО' 

        BoxLayout: 
            MDIconButton: 
                icon: 'phone' 
                disabled: True 
            SingleLineTextField: 
                id: number_field 
                hint_text: 'Номер' 

        BoxLayout: 
            MDIconButton: 
                icon: 'email' 
                disabled: True 
            SingleLineTextField: 
                id: email_field 
                hint_text: 'E-mail' 

    Widget: 
        size_hint: 1, .3 

    AnchorLayout: 
        anchor_x: 'right' 
        anchor_y: 'bottom' 
        size_hint: 1, None 
        height: dp(40) 
        MDFlatButton: 
            id: button_ok 
            text: 'OK' 
            on_release: app.save_info_contact()

emptyscreen.py


from kivy.uix.floatlayout import FloatLayout 
from kivy.properties import StringProperty, ObjectProperty, BooleanProperty 

class EmptyScreen(FloatLayout): 
    image = StringProperty() 
    text = StringProperty() 
    callback = ObjectProperty() 
    disabled = BooleanProperty()

createcontact.py


from kivy.uix.boxlayout import BoxLayout 

class CreateContact(BoxLayout): 
    pass

В EmptyScreen мы использовали еще один виджет из библиотеки KivyMD — MDFloatingActionButton, который стоит описать. Та самая назойливая муха, которую многим пользователям хочется прихлопнуть:


ffff67386f2f405ebad9856b9a94e4c5.png

    MDFloatingActionButton: 
        id: float_act_btn 
        icon: 'plus' 
        size_hint: None, None 
        size: dp(56), dp(56) 
        opposite_colors: True  # иконка белого/черного цветов 
        elevation_normal: 8  # длинна тени 
        pos_hint: {'center_x': .9, 'center_y': .1}  # самое нужное место на экране, которое кнопка обязательно закроет
        background_color: app.data.floating_button_color 
        background_color_down: app.data.floating_button_down_color 
        disabled: root.disabled 
        on_release: root.callback()

В CreateContact используются виджеты библиотеки KivyMD:


MDIconButton:


e4271fb867e1446f9d1e37a40758135f.png

MDIconButton — это кнопка с векторной иконкой. Полный набор официальных иконок от Google смотрите по ссылке. Все они используются и доступны в KivyMD.


SingleLineTextField:


6dfea6e57c9b4f2e97b858597dfe14de.png

MDFlatButton:


7acbee2dc17c49788c466299ef816e98.png

Будет вызывать функцию сохранения введенных пользователем данных:


        MDFlatButton: 
            …

            on_release: app.save_info_contact()

Получать введенную пользователем информацию из полей SingleLineTextField мы будем по уже описанному способу выше — по их id из аттрибута text:


DemoKivyContacts/libs/uix/kv/createcontact.kv


0dbe9d39e1504bd0b858a0d7ba40a77d.png

DemoKivyContacts/libs/programclass/showformcreatecontact.py


    def show_form_create_contact(self, *args): 
        '''Выводит на экран форму для создания нового контакта.''' 

        self.manager_tab_contacts.current = 'create_contact' 
        #  
        self._form_create_contact = \ 
            self.manager_tab_contacts.current_screen.children[0]

        ...

    def save_info_contact(self): 
        '''Сохраняет информацию о новом контакте.''' 

        name_contact = self._form_create_contact.ids.name_field.text 
        number_contact = self._form_create_contact.ids.number_field.text 
        mail_contact = self._form_create_contact.ids.email_field.text

        ...

После сохранения данных программа создает список контактов, если он не создан, или добавляет новый к уже существующему списку и выводит его на экран:


9c492b9774fa4157a374b6e83b2f94aa.png

show_contacts
    def show_contacts(self, info_contacts): 
        ''' 
        :type info_contacts: dict; 
        :param info_contacts: { 
            'Name contact': ['Number contact\nMail contact', 'path/to/avatar'] 
        }; 

        ''' 

        if not self._contacts_items: 
            # Создаем список контактов. 
            self._contacts_list = ContactsList() 
            self._contacts_items = Lists( 
                dict_items=info_contacts, flag='three_list_custom_icon', 
                right_icons=self.data.right_icons, 
                events_callback=self._event_contact_item 
            ) 

            button_add_contact = Builder.template( 
                'ButtonAdd', disabled=False, 
                events_callback=self.show_form_create_contact 
            ) 
            self._contacts_list.add_widget(self._contacts_items) 
            self._contacts_list.add_widget(button_add_contact) 
            self.add_screens( 
                'contact_list', self.manager_tab_contacts, self._contacts_list 
            ) 
        else: 
            # Добавляет контакт к существующему списку
            # и выводит список на экран. 
            self._add_contact_item(info_contacts) 
            self.manager_tab_contacts.current = 'contact_list'

Обратите внимание на функцию add_screens — программное добавление нового Activity и установка его в качестве текущего экрана:


DemoKivyContacts/program.py


    def add_screens(self, name_screen, screen_manager, new_screen): 
        screen = Screen(name=name_screen)  # cоздаем новый экран
        screen.add_widget(new_screen)  #  добавляем Activity в созданный экран
        screen_manager.add_widget(screen)  #  добавляем экран в менеджер экранов
        screen_manager.current = name_screen  #  указываем менеджеру имя Activity, которое должно стать  текущим экраном приложения

Я написал небольшую (пока топорную) обвязку для создания списков MDList — DemoKivyContacts/libs/uix/lists.py


Создать пункт списка с иконкой слева и векторными иконками справа можно достаточно легко, создав экземпляр класса Lists c нужными параметрами.


    def show_contacts(self, info_contacts): 
        ''' 
        :type info_contacts: dict; 
        :param info_contacts: { 
            'Name contact': ['Number contact\nMail contact', 'path/to/avatar'] 
        }; 

        ''' 

        …

        self._contacts_items = Lists( 
            dict_items=info_contacts, flag='three_list_custom_icon', 
            right_icons=self.data.right_icons, 
            events_callback=self._event_contact_item 
        )

6a5a151392a7473684cd477143fed733.png

Далее список self._contacts_items кидаете на любой требуемый виджет.


При создании пункта списка мы передали параметру events_callback функцию _event_contact_item для обработки событий путнкта:


    def _event_contact_item(self, *args): 
        '''События пункта списка контактов.''' 

        def end_call(): 
            self.screen.current = 'root_screen' 

        instanse_button = args[0] 
        if type(instanse_button) == RightButton: 
            name_contact, name_event = instanse_button.id.split(', ') 
            if name_event == 'call': 
                self.screen.current = 'call_contact' 
                data_contact = self.info_contacts[name_contact] 
                call_screen = self.screen.current_screen.children[0] 
                call_screen.name_contact = name_contact 
                call_screen.number_contact = data_contact[0].split('\n')[0] 
                call_screen.avatar = data_contact[1] 
                call_screen.callback = end_call 
            elif name_event == 'groups': 
                self._show_names_groups(name_contact) 
        else: 
            name_contact, name_event = args

Идентификаторы событий 'call' и 'group' — это имена иконок, которые мы указали в параметре right_icons:


DemoKivyContacts/libs/programdata.py


…

right_icons = ['data/images/call.png', 'data/images/groups.png']

При нажатии на иконку звонка откроется экран имитации исходящего вызова:


    def _event_contact_item(self, *args): 
        def end_call(): 
            self.screen.current = 'root_screen' 

        …

        if name_event == 'call': 
            self.screen.current = 'call_contact' 
            call_screen = self.screen.current_screen.children[0] 

            …

            call_screen.callback = end_call

f5ddfb51c2d144499b319507279d98e8.png

Все виджеты в нем уже описаны, поэтому просто приведу макет разметки данного Activity:


show_contacts
#:kivy 1.9.1 
#:import MDIconButton kivymd.button.MDIconButton 
#:import MDFloatingActionButton kivymd.button.MDFloatingActionButton 
#:import MDLabel kivymd.label.MDLabel 

: 
    id: call_contact 

    Widget: 
        id: title_line 

        canvas: 
            Color: 
                rgba: app.theme_cls.primary_color 
            Rectangle: 
                size: self.size 
                pos: self.pos 

        size_hint_y: None 
        height: root.height * 30 // 100  # 30% от высоты экрана 
        pos: 0, call_contact.height - self.size[1] 

    Widget: 
        canvas: 
            Ellipse: 
                pos: self.pos 
                size: 150, 150 
                source: root.avatar if root.avatar else 'data/logo/kivy-icon-128.png' 
        pos: (call_contact.width // 2) - 75, call_contact.height * 61 // 100 

    BoxLayout: 
        orientation: 'vertical' 
        size_hint: 1, None 
        height: 50
        pos: self.pos[0], call_contact.height * 45 // 100 

        MDLabel: 
            id: name_contact 
            font_style: 'Headline' 
            theme_text_color: 'Primary' 
            color: app.data.text_color 
            text: root.name_contact if root.name_contact else 'Abonent' 
            halign: 'center' 
        MDLabel: 
            id: number_contact 
            font_style: 'Subhead' 
            theme_text_color: 'Primary' 
            color: app.data.text_color 
            text: root.number_contact if root.number_contact else '12345' 
            halign: 'center' 

    BoxLayout: 
        size_hint: None, None 
        height: 60 
        width: volume.width + dialpad.width + account.width + mic.width 
        pos: (call_contact.width // 2) - (self.width // 2), call_contact.height * 18 // 100 

        MDIconButton: 
            id: volume 
            icon: 'volume-mute' 
        MDIconButton: 
            id: dialpad 
            icon: 'dialpad' 
        MDIconButton: 
            id: account 
            icon: 'account' 
        MDIconButton: 
            id: mic 
            icon: 'mic' 

    MDFloatingActionButton: 
        id: phone_end 
        icon: 'phone-end' 
        size_hint: None, None 
        size: dp(56), dp(56) 
        opposite_colors: True  # иконка белого/черного цветов 
        elevation_normal: 8  # длинна тени 
        pos_hint: {'center_x': .5, 'center_y': .1} 
        background_color: app.data.floating_button_color_end_call 
        background_color_down: app.data.floating_button_down_color_end_call 
        on_release: root.callback()

Виджет CallContact унаследован от FloatLayout:


from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty, ObjectProperty

class CallContact(FloatLayout):
    callback = ObjectProperty(lambda: None)
    avatar = StringProperty(None)
    name_contact = StringProperty(None)
    number_contact = StringProperty(None)

Это значит, что все виджеты и контроллы в нем будут накладываться друг на друга, отчего в разметке я использовал процентное указание их позиций относительно высоты главного экрана:


pos: self.pos[0], call_contact.height * 45 // 100 

Теперь, когда вы знаете, как работает ScreenManager, давайте еще раз взглянем на управляющий класс стартового Activity:


from kivy.uix.screenmanager import ScreenManager
from kivy.properties import ObjectProperty

class StartScreen(ScreenManager):
    events_callback = ObjectProperty(lambda: None)
    '''Функция обработки сигналов экрана.'''

и скелет разметки:


:
    Screen:
        name: 'root_screen'
        …
        # Экран с вкладками — MDTabbedPanel

    Screen:
        name: 'call_contact'
        CallContact:

То есть, при нажатии кнопки вызова в пункте списка контактов мы открываем Activity имитации исходящего вызова и закрываем его при нажатии кнопки «Отбой»:


    def _event_contact_item(self, *args): 
        def end_call(): 
            self.screen.current = 'root_screen' 

        …

        if name_event == 'call': 
            self.screen.current = 'call_contact' 
            call_screen = self.screen.current_screen.children[0] 

            …

            call_screen.callback = end_call

Процесс создания группы мы не будем рассматривать, так как он аналогичен процессу создания нового контакта. А остановимся на виджете NavigationDrawer:


9f13ae55c4464c87b4c882ca9cbc24c8.png

Для использования панели NavigationDrawer мы должны создать ее разметку и управляющий класс, унаследованный от NavigationDrawer:


nawdrawer.kv
#:kivy 1.9.1

:
    NavigationDrawerIconButton:
        icon: 'settings'
        text: app.data.string_lang_settings
        on_release: app.events_program(self.text)
    NavigationDrawerIconButton:
        icon: 'view-module'
        text: app.data.string_lang_plugin
        on_release: app.events_program(self.text)
    NavigationDrawerIconButton:
        icon: 'info'
        text: app.data.string_lang_license
        on_release: app.events_program(self.text)
    NavigationDrawerIconButton:
        icon: 'collection-text'
        text: 'About'
        on_release: app.events_program(self.text)
    NavigationDrawerIconButton:
        icon: 'close-circle'
        text: app.data.string_lang_exit_key
        on_release: app.events_program(app.data.string_lang_exit_key)

DemoKivyContacts/program.py


from kivy.app import App
from kivy.properties import ObjectProperty

from kivymd.navigationdrawer import NavigationDrawer

class NavDrawer(NavigationDrawer):
    events_callback = ObjectProperty()

class Program(App):
    nav_drawer = ObjectProperty()

    def __init__(self, **kvargs):
        super(Program, self).__init__(**kvargs)

    def build(self):
        self.nav_drawer = NavDrawer(title=data.string_lang_menu)

На этом пока все. С полным сценарием проекта вы можете ознакомиться на github.


P.S

Без сомнения библиотека KivyMD является отличным дополнением к фреймворку Kivy! Надеюсь, вы ее освоите и будете применять в своих проектах.


У меня есть предложение поменять формат статей о разработке мобильных приложений с помощью Kivy: возьмем уже готовое нативное приложение для Android, написанное на Java и создадим аналогичное, но написанное на Python с использованием фреймворка Kivy, по ходу действия освещая весь процесс разработки с нуля: как создаются виджеты и контроллы в Kivy, как использовать динамические классы, что есть FloatLayout и т.д.


Комментарии (3)

  • 20 октября 2016 в 12:56

    0

    Спасибо за статью! А можно где-то готовую .apk получить? Я с интересом слежу за Kivy (и Python — это мой любимейший ЯП), но скорость загрузки приложения у него хромала последний раз когда я его пробовал, так что ваша фраза:


    Используя в своих проектах библиотеку KivyMD плюс немного фантазии, вряд ли кто-то сможет визуально отличить, написана ли ваша программа на Java или с использованием фрейворка Kivy и Python.

    вызывает у меня определённые сомнения в честности всего материала.

    • 20 октября 2016 в 13:05

      0

      Под «вряд ли кто-то сможет визуально отличить» я не имел в виду скорость загрузки приложения, которая в настоящий момент имеет интервал в 4 секунды. apk я не собирал за ненадобностью. Тем более, что сценарий демонстрационного приложения написан на Python 3, для которого сборку установочного пакета под Android я пока не тестировал.

  • 20 октября 2016 в 13:00

    0

    Спасибо за статью! Немного работаю с Киви, но даже не подозревал о подобной библиотеке. Продолжайте писать такие статьи! Особенно на тему Киви :-)

© Habrahabr.ru