Write a post

Enjoy this post? Give Kuldeep a like if it's helpful.

8
5

Customizing your Navigation Drawer in Kivy & KivyMD

Published Jan 12, 2017Last updated Jun 02, 2017
Customizing your Navigation Drawer in Kivy & KivyMD

Kivy & KivyMD: NavigationDrawer

Kivy is an open source, cross-platform Python framework for the development of applications that makes use of innovative, multi-touch user interfaces.

KivyMD is a collection of Material Design compliant widgets for use with Kivy.

Prerequisites

This tutorial is meant for those who have a little or good familiarity with Kivy but don't know how to move forward with implementing their own widgets or for those who don't find Kivy visually attractive.

Some really cool resources for you:

Content

  • KivyMD's Navigation Drawer.
  • Modify the Navigation Drawer by replacing the Title with a Circular image

Structure

Before you start, make sure that you have this file structure.
Download the files from here.

- navigationdrawer
    - __init__.py  # our modified navigarion drawer.
- kivymd
    - ...
    - navigationdrawer.py
    - ...
- images # contains the image
    - me.jpg
- main.py

Before we start let's see how our main.py looks like.

  • NavigateApp class
    • Navigator's object
    • Theme class's object
  • Navigator class
    • NavigationDrawerIconButton

And we also have:

kivy

main.py

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.properties import ObjectProperty, StringProperty
    from kivymd.theming import ThemeManager
    from kivymd.navigationdrawer import NavigationDrawer
    # from navigationdrawer import NavigationDrawer

    main_widget_kv = '''
    #:import Toolbar kivymd.toolbar.Toolbar

    BoxLayout:
        orientation: 'vertical'
        Toolbar:
            id: toolbar
            title: 'Welcome'
            background_color: app.theme_cls.primary_dark
            left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]]
            right_action_items: [['more-vert', lambda x: app.raised_button.open(self.parent)]]
        Label:

    <Navigator>:
        NavigationDrawerIconButton:
            icon: 'face'
            text: 'Kuldeep Singh'
        NavigationDrawerIconButton:
            icon: 'email'
            text: 'kuldeepbb.grewal@gmail.com'
            on_release: app.root.ids.scr_mngr.current = 'bottomsheet'
        NavigationDrawerIconButton:
            icon: 'phone'
            text: '+91-7727XXXXXX'
        NavigationDrawerIconButton:
            icon: 'cake'
            text: '26/11/1994'
        NavigationDrawerIconButton:
            icon: 'city-alt'
            text: 'Rohtak'
        NavigationDrawerIconButton:
            icon: 'settings'
            text: 'Settings'
    '''

    class Navigator(NavigationDrawer):
        image_source = StringProperty('images/me.png')

    class NavigateApp(App):
        theme_cls = ThemeManager()
        nav_drawer = ObjectProperty()

        def build(self):
            main_widget = Builder.load_string(main_widget_kv)
            self.nav_drawer = Navigator()
            return main_widget

    NavigateApp().run()

Now that we have seen how the Navigation Drawer looks like, let's look at its source code.


kivymd/navigationdrawer.py

    # -*- coding: utf-8 -*-
    from kivy.lang import Builder
    from kivymd.label import MDLabel 
    from kivy.animation import Animation
    from kivymd.slidingpanel import SlidingPanel
    from kivymd.icon_definitions import md_icons
    from kivymd.theming import ThemableBehavior
    from kivymd.elevationbehavior import ElevationBehavior
    from kivy.properties import StringProperty, ObjectProperty
    from kivymd.list import OneLineIconListItem, ILeftBody, BaseListItem

    Builder.load_string('''
    <NavDrawerToolbar@Toolbar>
        canvas:
            Color:
                rgba: root.theme_cls.divider_color
            Line:
                points: self.x, self.y, self.x+self.width,self.y

    <NavigationDrawer>
        _list: list
        elevation: 0
        canvas:
            Color:
                rgba: root.theme_cls.bg_light
            Rectangle:
                size: root.size
                pos: root.pos
        NavDrawerToolbar:
            title: root.title
            opposite_colors: False
            title_theme_color: 'Secondary'
            background_color: root.theme_cls.bg_light
            elevation: 0
        ScrollView:
            do_scroll_x: False
            MDList:
                id: ml
                id: list

    <NavigationDrawerIconButton>
        NDIconLabel:
            id: _icon
            font_style: 'Icon'
            theme_text_color: 'Secondary'
    ''')

    class NavigationDrawer(SlidingPanel, ThemableBehavior, ElevationBehavior):
        title = StringProperty()
        _list = ObjectProperty()

        def add_widget(self, widget, index=0):
            if issubclass(widget.__class__, BaseListItem):
                self._list.add_widget(widget, index)
                widget.bind(on_release=lambda x: self.toggle())
            else:
                super(NavigationDrawer, self).add_widget(widget, index)

        def _get_main_animation(self, duration, t, x, is_closing):
            a = super(NavigationDrawer, self)._get_main_animation(duration, t, x,
                                                                  is_closing)
            a &= Animation(elevation=0 if is_closing else 5, t=t, duration=duration)
            return a

    class NDIconLabel(ILeftBody, MDLabel):
        pass

    class NavigationDrawerIconButton(OneLineIconListItem):
        icon = StringProperty()

        def on_icon(self, instance, value):
            self.ids['_icon'].text = u"{}".format(md_icons[value])

Now, NavigationDrawer class has a widget NavDrawerToolbar containing Title property.
We want to add a Circular Image there.

How to do it? By modifying the NavigationDrawer class.

Modify the Navigation Drawer by replacing the title with a circular image

Modification in the kv lang.

Original:

    <NavigationDrawer>
        ...
        NavDrawerToolbar:
            title: root.title
            opposite_colors: False
            title_theme_color: 'Secondary'
            background_color: root.theme_cls.bg_light
            elevation: 0
        ...

Modified:

    <NavigationDrawer>
        ...
        BoxLayout:
            size_hint: (1, .4)
            NavDrawerToolbar:
                padding: 10, 10
                canvas.after:
                    Color:
                        rgba: (1, 1, 1, 1)
                    RoundedRectangle:
                        size: (self.size[1]-dp(14), self.size[1]-dp(14))
                        pos: (self.pos[0]+(self.size[0]-self.size[1])/2, self.pos[1]+dp(7))
                        source: root.image_source
                        radius: [self.size[1]-(self.size[1]/2)]
        ...

Modification on the Python side.

Original:

    class NavigationDrawer(SlidingPanel, ThemableBehavior, ElevationBehavior):
        title = StringProperty()
        ...

Modified:

    class NavigationDrawer(SlidingPanel, ThemableBehavior, ElevationBehavior):
        image_source = StringProperty()
        ...

Modified Navigationdrawer.py

navigationdrawer/__init__.py

    # -*- coding: utf-8 -*-
    from kivy.animation import Animation
    from kivy.lang import Builder
    from kivy.properties import StringProperty, ObjectProperty
    from kivymd.elevationbehavior import ElevationBehavior
    from kivymd.icon_definitions import md_icons
    from kivymd.label import MDLabel
    from kivymd.list import OneLineIconListItem, ILeftBody, BaseListItem
    from kivymd.slidingpanel import SlidingPanel
    from kivymd.theming import ThemableBehavior

    Builder.load_string('''
    <NavDrawerToolbar@Label>
        canvas:
            Color:
                rgba: root.parent.parent.theme_cls.divider_color
            Line:
                points: self.x, self.y, self.x+self.width,self.y

    <NavigationDrawer>
        _list: list
        elevation: 0
        canvas:
            Color:
                rgba: root.theme_cls.bg_light
            Rectangle:
                size: root.size
                pos: root.pos
        BoxLayout:
            size_hint: (1, .4)
            NavDrawerToolbar:
                padding: 10, 10
                canvas.after:
                    Color:
                        rgba: (1, 1, 1, 1)
                    RoundedRectangle:
                        size: (self.size[1]-dp(14), self.size[1]-dp(14))
                        pos: (self.pos[0]+(self.size[0]-self.size[1])/2, self.pos[1]+dp(7))
                        source: root.image_source
                        radius: [self.size[1]-(self.size[1]/2)]

        ScrollView:
            do_scroll_x: False
            MDList:
                id: ml
                id: list

    <NavigationDrawerIconButton>
        NDIconLabel:
            id: _icon
            font_style: 'Icon'
            theme_text_color: 'Secondary'
    ''')

    class NavigationDrawer(SlidingPanel, ThemableBehavior, ElevationBehavior):
        image_source = StringProperty()
        _list = ObjectProperty()

        def add_widget(self, widget, index=0):
            if issubclass(widget.__class__, BaseListItem):
                self._list.add_widget(widget, index)
                widget.bind(on_release=lambda x: self.toggle())
            else:
                super(NavigationDrawer, self).add_widget(widget, index)

        def _get_main_animation(self, duration, t, x, is_closing):
            a = super(NavigationDrawer, self)._get_main_animation(duration, t, x,
                                                                  is_closing)
            a &= Animation(elevation=0 if is_closing else 5, t=t, duration=duration)
            return a

    class NDIconLabel(ILeftBody, MDLabel):
        pass

    class NavigationDrawerIconButton(OneLineIconListItem):
        icon = StringProperty()

        def on_icon(self, instance, value):
            self.ids['_icon'].text = u"{}".format(md_icons[value])

Now that we have modified our Navigation Drawer let's test it. but before let's make the following changes:

  • Uncomment NavigationDrawer from the navigationdrawer folder
  • Comment out the NavigationDrawer from the kivymd in the main.py file.
    # from kivymd.navigationdrawer import NavigationDrawer
    from navigationdrawer import NavigationDrawer

And here it is. Our Navigation Drawer with a circular image.

kivy

Conslusion

Thank you for reading — I hope you found this post helpful. If you have any questions, feel free to reach out to me! (More posts)

Discover and read more posts from Kuldeep
get started
Enjoy this post?

Leave a like and comment for Kuldeep

8
5
5Replies
Techfreak Festa
a month ago

i am getting this error

from kivymd.navigationdrawer import NavigationDrawer
ImportError: cannot import name NavigationDrawer

Gabriel Pettier
4 months ago

Funny to use a RoundedRectangle, when an Ellipse would have done, but hey, it works ^^.

Kuldeep
4 months ago

Yeah! but since RoundedRectangle was added in 1.9.1, I wanted to show that instead of Ellipse.

Engenheiro Jonnathan Lopes
5 months ago

How can I put the navigatordrawer on a specified screen within my application. Example I have the ‘login’, ‘register’ and ‘home’ screens and I want to put it on the ‘home’ screen only after login.

Kuldeep
4 months ago

Add the navigation drawer to the screen and then add the screen to the screen manager, that should work. :)

Show more replies

Subscribe to our weekly newsletter