Codementor Events

Customizing your Navigation Drawer in Kivy & KivyMD

Published Jan 12, 2017Last updated Jun 10, 2018
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 this post — I hope you found this helpful. You can find me on GitHub, LinkedIn and CodeMentor. If you have any questions, feel free to reach out to me!
More posts:

Discover and read more posts from Kuldeep
get started
post comments11Replies
Oladunjoye Victor Olaoluwa
3 years ago

How can I use the MDNavigationDrawer from the main.py not .kv

pawar jay
5 years ago

i have showing the (AttributeError: ‘Toolbar’ object has no attribute ‘toggle’

error ,) than what can i doing here brothre

surbhi s
6 years ago

AttributeError: ‘NavigateApp’ object has no attribute ‘raised_button’

Show more replies