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:
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.
Navigationdrawer.py from KivyMD. (Source)
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])
Here we see that NavigationDrawer
class has a widget named as NavDrawerToolbar
which contains the 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 you do, make sure you uncomment NavigationDrawer
from the navigationdrawer folder
and 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.
About me
My online existence is mainly here at Codementor and at these places :)
Thank you.