Приветствую, уважаемые любители и знатоки Python!

Если вы используете в своих приложениях Python и такие кроссплатформенные инструменты как фреймворк Kivy и библиотеку материального дизайна KivyMD, тогда вам точно будет интересно эта статья, в которой я расскажу о дополнительных компонентах для расширения набора материальных виджетов библиотеки KivyMD

Совсем недавно мы (команда разработчиков KivyMD) создали на GitHub KivyMD-Extension — организацию, в которой размещаются репозитории пользовательских дополнений для библиотеки KivyMD. Это пакеты компонентов, которые не связаны напрямую со спецификацией материального дизайна, но используют под капотом библиотеку KivyMD и существенно расширяют ее. О нескольких таких пакетах я расскажу сегодня.

AKivyMD


AKivyMD (Awesome KivyMD) — расширение с большим количеством виджетов, анимаций и прочих красивостей:

Bottom navigation


Пример кода
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen

from kivymd_extensions.akivymd import *

Builder.load_string(
    """
<On_active_button@Button_Item>
    icon_color: app.theme_cls.text_color
    text_color: app.theme_cls.text_color
    button_bg_color: app.theme_cls.primary_color
    mode: "color_on_active"
    badge_disabled: True


<BottomNavigation>
    name: "BottomNavigation"

    MDBoxLayout:
        orientation: "vertical"
        spacing: "40dp"

        MDToolbar:
            title: root.name
            left_action_items: [["arrow-left" , lambda x: x]]

        AKBottomNavigation2:
            bg_color: app.theme_cls.bg_darkest

            On_active_button:
                text: "Alert"
                icon: "bell-outline"

            On_active_button:
                text: "Bank"
                icon: "bank-outline"

            On_active_button:
                text: "Download"
                icon: "arrow-down-bold-outline"

        AKBottomNavigation2:
            bg_color: app.theme_cls.bg_darkest

            Button_Item:
                text: "Alert"
                icon: "bell-outline"
                icon_color: .3, .2, .3, 1
                text_color: .3, .2, .3, 1
                button_bg_color: .7, .5, .7, 1
                badge_text: "+12"

            Button_Item:
                text: "Bank"
                badge_text: ""
                icon: "bank-outline"
                icon_color: .2, .2, .6, 1
                text_color: .2, .2, .6, 1
                button_bg_color: .6, .6, 1, 1

            Button_Item:
                text: "Download"
                icon: "arrow-down-bold-outline"
                icon_color: .8, 0, 0, 1
                text_color: .8, 0, 0, 1
                button_bg_color: 1, .6, .6, 1
                badge_disabled: True

        Widget:

        AKBottomNavigation:
            items: root.bottomnavigation_items
"""
)


class BottomNavigation(MDScreen):
    bottomnavigation_items = [
        {"icon": "android", "text": "android", "on_release": lambda x: None},
        {"icon": "menu", "text": "menu", "on_release": lambda x: None},
        {"icon": "account", "text": "account", "on_release": lambda x: None},
    ]


class Test(MDApp):
    def build(self):
        return BottomNavigation()


Test().run()


Spinners


Пример кода
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen

from kivymd_extensions.akivymd import *

Builder.load_string(
    """
<SpinnerBox@MDBoxLayout>
    adaptive_height: True
    text: ""

    MDLabel:
        theme_text_color: "Primary"
        text: root.text
        size_hint_x: None
        width: "200dp"
        halign: "center"
        valign: "center"


<Spinners>
    name: "Spinners"

    on_leave:
        root.stop_animation()

    MDBoxLayout:
        orientation: "vertical"

        MDToolbar:
            title: root.name
            left_action_items: [["arrow-left", lambda x: x]]

        MDFloatLayout:
            padding: "10dp"

            AKSpinnerCircleFlip:
                id: circleflip
                spinner_size: "30dp"
                pos_hint: {"center_x": .5, "top": .9}

            AKSpinnerDoubleBounce:
                id:doublebounce
                spinner_size: "30dp"
                pos_hint: {"center_x": .5, "center_y": .7}

            AKSpinnerFoldingCube:
                id: foldingcube
                spinner_size: "40dp"
                pos_hint: {"center_x": .5, "center_y": .4}
                angle: 45

            AKSpinnerThreeDots:
                id: threedots
                spinner_size: "20dp"
                pos_hint: {"center_x": .5, "y": .1}

            Widget:

    MDBoxLayout:
        padding: "5dp"
        spacing: "5dp"

        MDRaisedButton:
            text: "Start"
            on_release: root.start_animation()

        MDRaisedButton:
            text: "Stop"
            on_release: root.stop_animation()
"""
)


class Spinners(MDScreen):
    def stop_animation(self):
        ids = self.ids
        ids.foldingcube.active = False
        ids.threedots.active = False
        ids.doublebounce.active = False
        ids.circleflip.active = False

    def start_animation(self):
        ids = self.ids
        ids.foldingcube.active = True
        ids.threedots.active = True
        ids.doublebounce.active = True
        ids.circleflip.active = True


class Test(MDApp):
    def build(self):
        return Spinners()


Test().run()


Piechart



Пример кода
from kivy.lang import Builder
from kivy.metrics import dp

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen

from kivymd_extensions.akivymd.uix.piechart import AKPieChart

Builder.load_string(
    """
<Piechart>
    on_leave: root.remove_chart()
    name: "Piechart"

    MDBoxLayout:
        orientation: "vertical"

        MDToolbar:
            title: root.name
            left_action_items: [["arrow-left", lambda x: x]]

        ScrollView:

            MDBoxLayout:
                id: chart_box
                adaptive_height: True
                padding: "24dp"
                orientation: "vertical"

        MDBoxLayout:
            adaptive_height: True
            padding: "4dp"
            spacing: "4dp"

            MDRaisedButton:
                text: "Update"
                on_release: root.update_chart()
"""
)


class Piechart(MDScreen):
    items = [{"Python": 40, "Java": 30, "C++": 10, "PHP": 8, "Ruby": 12}]

    def __init__(self, **kw):
        super().__init__(**kw)

    def on_enter(self):
        self.piechart = AKPieChart(
            items=self.items,
            pos_hint={"center_x": 0.5, "center_y": 0.5},
            size_hint=[None, None],
            size=(dp(300), dp(300)),
        )
        self.ids.chart_box.add_widget(self.piechart)

    def update_chart(self):
        self.piechart.items = [{"Python": 70, "Dart": 10, "C#": 10, "Css": 10}]

    def remove_chart(self):
        self.ids.chart_box.remove_widget(self.piechart)


class Test(MDApp):
    def build(self):
        self.piechart = Piechart()
        return self.piechart

    def on_start(self):
        self.piechart.dispatch("on_enter")


Test().run()


Onboarding


Пример кода
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen

from kivymd_extensions.akivymd import *

Builder.load_string(
    """
<MyAKOnboardingItem@AKOnboardingItem>
    source: ""
    text: ""
    title: ""

    MDFloatLayout:

        Image:
            source: root.source
            pos_hint: {"center_x": .5, "y": .6}
            size_hint: .4, .3

        MDBoxLayout:
            orientation: "vertical"
            padding: dp(10)
            adaptive_height: True
            pos_hint: {"center_x": .5, "top": .5}
            spacing: dp(20)
            size_hint_x: .7

            canvas.before:
                Color:
                    rgba: app.theme_cls.primary_dark
                RoundedRectangle:
                    pos: self.pos
                    size: self.size

            MDLabel:
                text: root.title
                bold: True
                size_hint_y: None
                height: self.texture_size[1]
                theme_text_color: "Primary"
                font_style: "H6"
                halign: "center"
                valign: "center"

            MDLabel:
                size_hint_y: None
                height: self.texture_size[1]
                theme_text_color: "Primary"
                font_style: "Body1"
                halign: "center"
                valign: "center"
                text: root.text


<Onboarding>
    name: "Onboarding"
    on_leave: boarding.reset()

    MDBoxLayout:
        orientation: "vertical"
        spacing: dp(10)

        AKOnboarding:
            id: boarding
            on_finish: root.finish_callback()
            circles_size: dp(15)

            MyAKOnboardingItem:
                source: "assets/slide_one_img.png"
                text:
                    "Lorem Ipsum is simply dummy text of the printing and "                     "typesetting industry. Lorem Ipsum has been the industry's "                     "standard dummy text ever since the 1500s, when an unknown "                     "printer took a galley of type and scrambled it to make a type specimen book"
                title:"What is Lorem Ipsum?"

            MyAKOnboardingItem:
                source: "assets/slide_two_img.png"
                text:
                    "It is a long established fact that a reader will be distracted "                     "by the readable content of a page when looking at its layout."
                title: "Why do we use it?"

            MyAKOnboardingItem:
                source: "assets/slide_three_img.png"
                text:
                    "Contrary to popular belief, Lorem Ipsum is not simply random text. "                     "It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old."
                title: "Where does it come from?"
"""
)


class Onboarding(MDScreen):
    def finish_callback(self):
        print("Finish callback")


class Test(MDApp):
    def build(self):
        return Onboarding()


Test().run()


Navigationrail


Пример кода
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen

from kivymd_extensions.akivymd import *

Builder.load_string(
    """
<Navigationrail>
    name: "Navigationrail"

    AKNavigationrail:
        id: rail

        AKNavigationrailCustomItem:
            size_hint_y: None
            height: dp(60)
            padding: dp(5)

            MDIconButton:
                icon: "arrow-left"
                theme_text_color: "Custom"
                text_color: 1, 1, 1, 1
                on_release:
                    if rail.get_state() == "open": rail.dismiss(); self.icon="menu"
                    else: rail.open(); self.icon ="arrow-left"

        AKNavigationrailItem:
            text: "Withdraw"
            icon: "wallet-plus"
            on_release: scr_mng.current= "withdraw"

        AKNavigationrailItem:
            text: "Deposit"
            icon: "wallet-plus-outline"
            on_release: scr_mng.current= "deposit"

        AKNavigationrailItem:
            text: "Profile"
            icon: "account-circle-outline"
            on_release: scr_mng.current= "profile"

        AKNavigationrailCustomItem:

        AKNavigationrailContent:

            ScreenManager:
                id: scr_mng

                MDScreen:
                    name: "withdraw"
                    MDLabel:
                        text: "Withdraw"
                        halign: "center"

                MDScreen:
                    name: "deposit"

                    MDLabel:
                        text: "Deposit"
                        halign: "center"

                MDScreen:
                    name: "profile"

                    MDLabel:
                        text: "Profile"
                        halign: "center"
"""
)


class Navigationrail(MDScreen):
    pass


class Test(MDApp):
    def build(self):
        return Navigationrail()


Test().run()


Raiting



Пример кода
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen

from kivymd_extensions.akivymd import *

Builder.load_string(
    """
<Rating>:
    name: "Rating"
    BoxLayout:
        orientation: "vertical"
        MDToolbar:
            title: root.name
            left_action_items:[["arrow-left" , lambda x: x]]

        FloatLayout:
            AKRating:
                normal_icon: "star-circle-outline"
                active_icon: "star-circle"
                pos_hint: {"center_x": .5, "center_y": .3}
                on_rate: print(self.get_rate())

            AKRating:
                pos_hint: {"center_x": .5, "center_y": .5}
                on_rate: print(self.get_rate())
                direction: "rl"

            AKRating:
                normal_icon: "star-box-outline"
                active_icon: "star-box"
                active_color: 1,0,0.4,1
                animation_type: "grow"
                pos_hint: {"center_x": .5, "center_y": .7}
                on_rate: print(self.get_rate())
    """
)


class Rating(MDScreen):
    pass


class Test(MDApp):
    def build(self):
        return Rating()


Test().run()


SilverAppbar


Пример кода
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.list import OneLineListItem
from kivymd.uix.screen import MDScreen

from kivymd_extensions.akivymd import *

Builder.load_string(
    """
<SilverAppbar>
    name: "SilverAppbar"

    AKSilverAppbar:
        max_height: dp(300)
        title: root.name
        left_action_items: [["arrow-left", lambda x: x]]
        pin_top: True
        hide_toolbar: True
        radius: dp(20)
        toolbar_bg: app.theme_cls.primary_color

        AKSilverAppbarHeader:
            orientation: "vertical"

            Image:
                source: "assets/fly.jpg"
                allow_stretch: True
                keep_ratio: False

        AKSilverAppbarContent:
            padding: dp(10)
            id: content
            size_hint_y: None
            height: self.minimum_height
            orientation: "vertical"
            md_bg_color: app.theme_cls.primary_color
"""
)


class SilverAppbar(MDScreen):
    def on_enter(self, *args):
        for x in range(30):
            self.ids.content.add_widget(OneLineListItem(text="Item %d" % x))


class Test(MDApp):
    def build(self):
        self.sliver_bar = SilverAppbar()
        return self.sliver_bar

    def on_start(self):
        self.sliver_bar.dispatch("on_enter")


Test().run()


Это далеко не все компоненты пакета AKivyMD. Информацию по установке и других компонентах этого пакета вы можете посмотреть в его репозитории.

File Manager


File Manager — файловый менеджер для рабочего стола. Да, именно для Desktop, потому что KivyMD уже предоставляет из коробки файловый менеджер для выбора директорий и файлов. Нет, File Manager из KivyMD Extensions будет работать и на мобильных устройствах, но зачем? Поэтому мы не заморачиваемся, как это будет выглядеть на мобильном экране. Разработка тестируется только для Desktop:


Пример использования:

from kivymd.app import MDApp

from kivymd_extensions.filemanager import FileManager


class MainApp(MDApp):
    def on_start(self):
        FileManager().open()


if __name__ == "__main__":
    MainApp().run()

Sweet Alert


Sweet Alert — аналог диалоговых окон из пакета sweetalert2:


Пример использования:

from kivy.lang import Builder

from kivymd.app import MDApp

KV = """
#:import SweetAlert kivymd_extensions.sweetalert.SweetAlert


MDScreen:

    MDRaisedButton:
        text: "EXAMPLE"
        pos_hint: {"center_x": .5, "center_y": .5}
        on_release: SweetAlert().fire("Any fool can use a computer!")
"""


class MainApp(MDApp):
    def build(self):
        return Builder.load_string(KV)


if __name__ == "__main__":
    MainApp().run()


На этом мой небольшой обзор дополнительных компонентов для библиотеки KivyMD подошел к концу. Надеюсь, он был полезен.