How do I re-use MDNavigationDrawer without re-typing the code kivymd - kivy

I've got the below MDNavigationDrawer code but the problem is I repeat it on every screen, which is just making the code longer unessecarily. How do I write the below code once and re-use it on every screen?
<Example>:
name: "example"
NavigationLayout:
id: nav_layout
MDNavigationDrawer:
NavigationDrawerIconButton:
text: "My Conversations"
on_release: app.root.current = "conversations"
icon: "chat-outline"
NavigationDrawerIconButton:
text: "Personal Details"
on_release: app.root.current = "details"
icon: "settings"
NavigationDrawerIconButton:
text: "Logout"
on_release: root.clear_details()
on_release: app.root.current = "login"
icon: "account"

Try using a dynamic class:
<MyNavDrawer#MDNavigationDrawer>:
NavigationDrawerIconButton:
text: "My Conversations"
on_release: app.root.current = "conversations"
icon: "chat-outline"
NavigationDrawerIconButton:
text: "Personal Details"
on_release: app.root.current = "details"
icon: "settings"
NavigationDrawerIconButton:
text: "Logout"
on_release: root.clear_details()
on_release: app.root.current = "login"
icon: "account"
Then use it as:
<Example>:
name: "example"
NavigationLayout:
id: nav_layout
MyNavDrawer:
There may be an issue with the line on_release: root.clear_details(), since root may not end up pointing at the correct instance.

Related

How can I modify the title of the MDTopAppBar when a button, located in a separate class, is clicked?

How can I modify the title of the MDTopAppBar in response to a user selecting a button within the NavigationDrawer, such as updating the title to match the selected button's label (e.g. "Coffee")?
main.py
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
from kivymd.uix.scrollview import MDScrollView
class MainLayout(Screen):
pass
class NavigationDrawer(MDScrollView):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class App(MDApp):
def build(self):
self.theme_cls.primary_palette = "Green"
self.theme_cls.theme_style = "Light"
kivy_layout = Builder.load_file("layout.kv")
return MainLayout()
if __name__ == "__main__":
App().run()
layout.kv
<NavigationDrawer>
MDNavigationDrawerMenu:
MDNavigationDrawerHeader:
title: "MyApp"
text: "Bottom Text"
source: "icon64.png"
spacing: "10dp"
MDNavigationDrawerDivider:
MDNavigationDrawerItem:
text: "Coffee"
icon: "coffee"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 1"
MDNavigationDrawerItem:
text: "History"
icon: "history"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 2"
MDNavigationDrawerItem:
text: "Settings"
icon: "cog"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 3"
MDNavigationDrawerDivider:
<MainLayout>
MDScreen:
MDTopAppBar:
id: top
title: "Title"
pos_hint: {"top": 1}
elevation: 4
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
MDScreenManager:
id: screen_manager
MDScreen:
name: "scr 1"
MDLabel:
text: "Coffee"
halign: "center"
MDScreen:
name: "scr 2"
MDLabel:
text: "History"
halign: "center"
MDScreen:
name: "scr 3"
MDLabel:
text: "Settings"
halign: "center"
MDNavigationDrawer:
id: nav_drawer
radius: (0, 16, 16, 0)
NavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
After attempting to resolve the issue on my own through various means, including attempting to create a solution and conducting research online for potential solutions, I was unable to find a satisfactory resolution. As a result, I have determined it necessary to seek assistance through this platform.
You need to add an id to the MDNavigationDrawerHeader so you can access it from the class.
MDNavigationDrawerHeader:
id: drawer_header
title: "MyApp"
text: "Bottom Text"
source: "icon64.png"
spacing: "10dp"
Then you need to add a method to the NaviagtionDrawer class to make the update. You will access your NavDrawer Title by calling self.ids.drawer_header.title
class NavigationDrawer(MDScrollView):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
def set_title(self, title):
self.ids.drawer_header.title = title
Finally, you need to tell each button to call the set_title method that I created to actually do the update.
MDNavigationDrawerItem:
text: "Coffee"
icon: "coffee"
on_press:
root.nav_drawer.set_state("close")
root.set_title(self.text)
root.screen_manager.current = "scr 1"
To change the MDTopAppBar title add the following line to each of the on_press events:
app.change_title(self.text) # call method in main app
ie.
on_press:
root.nav_drawer.set_state("close")
root.set_title(self.text)
root.screen_manager.current = "scr 1"
app.change_title(self.text) # call method in main app
Then add the following method to your main app.
def change_title(self, title):
self.root.ids.top.title = title
ie.
def build(self):
self.theme_cls.primary_palette = "Green"
self.theme_cls.theme_style = "Light"
Builder.load_file("layout.kv")
return MainLayout()
def change_title(self, title):
self.root.ids.top.title = title

How to connect carousel with navigation bar in kivymd?

I am making an app in kivymd where there is a bottom navigation bar with three bottom nav elements(home, plus, profile). I want That on pressing home a carousel with a three line avatar list get's shown. But if a write my code under navigation element (home) it sort of comes in middle. And if I write the code inside main boxlayout the content of other buttons comes underneath.
following is my code with code under nav element being commented:
from kivymd.app import MDApp
from kivy.core.window import Window
from kivy.lang import Builder
Window.size = (375, 667)
kv = """
BoxLayout:
orientation:'vertical'
padding: 0
MDToolbar:
title: "Ad 'O Square"
md_bg_color: 1, 1, 1, 1
specific_text_color: app.theme_cls.accent_color
elevation: 10
Carousel:
direction: "right"
Image:
source: "Colors/red.jpg"
Image:
source: "Colors/green.png"
Image:
source: "Colors/blue.png"
ThreeLineAvatarListItem:
text: "Product 1"
secondary_text: "Short Text"
tertiary_text: "Rating"
ImageLeftWidget:
source: "Colors/red.jpg"
ThreeLineAvatarListItem:
text: "Product 2"
secondary_text: "Short Text"
tertiary_text: "Rating"
ImageLeftWidget:
source: "Colors/blue.png"
ThreeLineAvatarListItem:
text: "Product 3"
secondary_text: "Short Text"
tertiary_text: "Rating"
ImageLeftWidget:
source: "Colors/green.png"
MDBottomNavigation:
panel_color: 1, 1, 1, 1
MDBottomNavigationItem:
name: 'screen 1'
text: 'home'
icon: 'home'
# Carousel:
# direction: "right"
# Image:
# source: "Colors/red.jpg"
# Image:
# source: "Colors/green.png"
# Image:
# source: "Colors/blue.png"
# ThreeLineAvatarListItem:
# text: "Product 1"
# secondary_text: "Short Text"
# tertiary_text: "Rating"
#
# ImageLeftWidget:
# source: "Colors/red.jpg"
#
#
# ThreeLineAvatarListItem:
# text: "Product 2"
# secondary_text: "Short Text"
# tertiary_text: "Rating"
#
# ImageLeftWidget:
# source: "Colors/blue.png"
#
#
# ThreeLineAvatarListItem:
# text: "Product 3"
# secondary_text: "Short Text"
# tertiary_text: "Rating"
#
# ImageLeftWidget:
# source: "Colors/green.png"
MDBottomNavigationItem:
name: 'screen 2'
icon: 'plus-circle-outline'
MDLabel:
text: "Screen 2"
halign: "center"
MDBottomNavigationItem:
name: 'screen 3'
text: 'profile'
icon: 'account'
MDLabel:
text: 'Profile'
halign: 'center'
"""
class App(MDApp):
def build(self):
self.theme_cls.accent_palette = "Red"
screen = Builder.load_string(kv)
return screen
if __name__ == '__main__':
App().run()
this is the output
and this is the output when I write the code inside bottom navigation element

How to remove widget in Kivy and then create again?

I am using Kivy and Kivymd.
There is a class Detail in my code where I am creating a card. I need to remove any card before creating a new card.
I have a func remove. I can use it to remove any cards, then create a new card. But I need to implement removing and creating in one func add.
My py file
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.label import MDLabel
from kivymd.uix.card import MDCard
class WindowManager(ScreenManager):
pass
class Detail(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def add(self, instance):
# self.remove()
article = self.ids.article.text
card = MDCard(size_hint=(None, None), padding=20)
my_label = MDLabel(text=article)
card.add_widget(my_label)
self.ids.box.add_widget(card)
def remove(self):
self.ids.boxlayout_1.clear_widgets()
self.add_widget(Detail())
class Container(Screen):
pass
class App(MDApp):
def build(self):
return WindowManager()
App().run()
my app.kv
<WindowManager>:
Container:
name: 'container'
Detail:
name: 'detail'
<Container>
MDRaisedButton:
pos_hint: {'center_x': .5, 'center_y': .5}
text: 'Go To Detail'
on_release: root.manager.current = 'detail'
<Detail>
BoxLayout:
id: boxlayout_1
orientation: 'vertical'
MDToolbar:
title: 'App'
Button:
id: btn
text: 'Remove Cards'
on_release: root.remove()
MDTextField:
id: article
hint_text: 'Input article'
helper_text_mode: "on_focus"
mode: 'rectangle'
MDRaisedButton:
id: me
text: 'add'
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: root.add(root)
ScrollView:
MDList:
spacing: 5
id: box
Your remove() method is removing more than just the cards. Try changing it to just remove the Widgets in the MDList, like this:
def remove(self):
self.ids.box.clear_widgets()
Then the add() method can just call remove(), like this:
def add(self, instance):
self.remove()
article = self.ids.article.text
card = MDCard(size_hint=(None, None), padding=20)
my_label = MDLabel(text=article)
card.add_widget(my_label)
self.ids.box.add_widget(card)

Cannot access class variable outside class - kivy

Anytime i try to run my app i get error.
Error: AttributeError: 'NoneType' object has no attribute 'text'
See code snippet below
class Login(Screen):
email_obj = ObjectProperty()
password_obj = ObjectProperty()
conn = lite.connect("database/nairamanager.db")
cursor = conn.cursor()
def init(self, **kwargs):
super().__init__(**kwargs)
# Display an alert on login validation
def login_alert(self):
p = LoginAlert()
p.open()
# Get user from database
def login_nm_user(self):
self.cursor.execute("SELECT * FROM user_tb WHERE email_db = ? AND password_db = ?;", (self.email_obj.text, self.password_obj.text))
if self.cursor.fetchall():
self.manager.current = "news_feed_screen"
else:
return self.login_alert()
self.conn.commit()
# self.conn.close
# Get user email
def get_user_email(self):
self.cursor.execute("SELECT * FROM user_tb WHERE email_db = ?;", (self.email_obj.text))
self.user_email = self.cursor.fetchone()[0]
self.conn.commit()
print(self.user_email)
login = Login()
login.get_user_email()
print(login.user_email)
.kv code
Below is the Login class in the kivy lang file
<Login>:
email_obj: email_text
password_obj: password_text
GridLayout:
size: root.size
rows: 2
rows_minimum: {0:0,1:root.height*.7}
spacing: 50
GridLayout:
rows: 2
row_default_minimum: True
rows_minimum: {0:root.height*.1,1:root.height*.01}
BoxLayout:
orientation: "horizontal"
BackButton:
text: "L"
size_hint: (.1,1)
halign: "center"
markup: True
on_press:
root.manager.current = "login_signup_screen"
root.manager.transition.direction = "left"
TopLabel:
text: "[b]Lets get you logged in...[/b]"
size_hint: (.9,1)
BorderLabel:
# Login form
BoxLayout:
orientation: "vertical"
spacing: 20
UniTextInput:
id: email_text
hint_text: "Email"
size_hint: (.6,1)
pos_hint: {"center_x":.5}
UniTextInput:
id: password_text
hint_text: "Password"
password: True
size_hint: (.6,1)
pos_hint: {"center_x":.5}
GreenButton
id: main_login_button
text: "Login"
size_hint: (.6,1)
pos_hint: {"center_x":.5}
on_press:
root.login_nm_user()
root.get_user_email()
# Fake widget for spacing
Widget:
Widget:
Widget:
Please, how do i fix this?
To share a variable to all screens, you can make it an attribute of the screenmanager.
Like this:
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
from kivy.properties import StringProperty
class MyScreenManager(ScreenManager):
user_email = StringProperty()
class Login(Screen):
def login(self, user, pw):
# login
# if pass
self.manager.user_email = user
self.manager.current = "screen2"
root = Builder.load_string('''
MyScreenManager:
Login:
name: "login"
id: login
BoxLayout:
orientation: 'vertical'
TextInput:
id: email_input
TextInput:
id: password_input
Button:
text: "Login"
on_release: login.login(email_input.text, password_input.text)
Screen:
name: "screen2"
BoxLayout:
orientation: "vertical"
Label:
text: "Wellcome {}!".format(root.user_email)
Button:
text: "Go to screen 3"
on_release: root.current = "screen3"
Screen:
name: "screen3"
BoxLayout:
orientation: "vertical"
Label:
text: "Wellcome {}!".format(root.user_email)
Button:
text: "Go back to screen 2"
on_release: root.current = "screen2"
''')
class MyApp(App):
def build(self):
return root
MyApp().run()
That said, I see you define a init method. If you really want to overite the __init__, remember the underscores.
In this particular example, you dont really do anything in the init. super().__init__() gets executed anyways when you create a new Screen

Variables in Kv language (kivy)

My Kivy Language file has many font_size properties, all with same values, is there away where I can assign a variable in KV lang?
Current KV file sample:
#User ID
Label:
text: 'User ID'
font_size: 20
text_size: self.size
TextInput:
id: userid
font_size: 20
#User PW
Label:
text: 'Password'
font_size: 20
text_size: self.size
TextInput:
id: password
password: True
font_size: 20
Button:
text: 'Login'
font_size: 20
Is is possible to set it somewhat like this:
#User ID
#fs: 20
Label:
text: 'User ID'
font_size: fs
text_size: self.size
TextInput:
id: userid
font_size: fs
#User PW
Label:
text: 'Password'
font_size: fs
text_size: self.size
TextInput:
id: password
password: True
font_size: fs
Button:
text: 'Login'
font_size: fs
By doing so, I would be able to change the font size at once only by changing the FS variable value, also, similar solution might help me to create theme based files faster. Thank you.
I would be able to change the font size at once only by changing the FS variable value,
You can set a value with #:set name value, but this isn't quite what you want. Since you want the variable to update, you should use a kivy property so that the event system takes care of it for you.
In this case, since you want lots of different things to depend on size like that, you could for instance use a property of your app class.
class YourApp(App):
font_size = NumericProperty(20)
then in kv
font_size: app.font_size
Any changes to the font_size of the App instance will then automatically propagate to these kv rules.
Yes, there is a way. What you are looking for is this expression:
#:set name value
You can see the documentation here
Your .kv file:
#User ID
#:fs 20
Label:
text: 'User ID'
font_size: fs
text_size: self.size
TextInput:
id: userid
font_size: fs
#User PW
Label:
text: 'Password'
font_size: fs
text_size: self.size
TextInput:
id: password
password: True
font_size: fs
Button:
text: 'Login'
font_size: fs

Resources