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

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

Related

Change toolbar title when changing screens in kivymd

My program contains two screens. I wish to change the toolbar title whenever I change screens.
This is my main.py file
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.properties import ObjectProperty, StringProperty
from kivymd.uix.navigationdrawer import MDNavigationDrawer
from kivymd.uix.navigationdrawer import MDNavigationLayout
from kivy.uix.screenmanager import Screen
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager
from kivymd.uix.toolbar import MDToolbar
Window.size = (360,640)
global sm
class DashboardScreen(Screen):
def __init__(self, **kwargs):
super(DashboardScreen, self).__init__(**kwargs)
class DigitalGoldScreen(Screen):
def __init__(self, **kwargs):
super(DigitalGoldScreen, self).__init__(**kwargs)
class ContentNavigationDrawer(MDBoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
# Create the screen manager
sm = ScreenManager()
sm.add_widget(DashboardScreen(name='screen-dash'))
sm.add_widget(DigitalGoldScreen(name='screen-dgld'))
class MoneyManagerCopiedApp(MDApp):
def __init__(self, **kwargs):
super(MoneyManagerCopiedApp, self).__init__(**kwargs)
def set_toolbartitle(self, screen):
self.title = screen
return self.title
def get_toolbartitle(self):
screentitle = self.title
if screentitle == "screen-dash":
self.settitle = "Dashboard"
elif screentitle == "screen-dgld":
self.settitle = "Digital Gold"
else:
pass
return (self.settitle)
def build(self):
pass
print(sm.current)
if __name__ == "__main__":
MoneyManagerCopiedApp().run()
Here is my .kv file
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: "Digital Gold"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "screen-dgld"
OneLineListItem:
text: "Dashboard"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "screen-dash"
MDScreen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "dashboard"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
ScreenManager:
id: screen_manager
DashboardScreen:
DigitalGoldScreen:
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
<DashboardScreen>:
name: 'screen-dash'
MDRectangleFlatButton:
text: "You're in dashboard screen"
pos_hint: {'center_x':0.5,'center_y':0.6}
on_press: root.manager.current = 'screen-dgld'
<DigitalGoldScreen>:
name: 'screen-dgld'
MDLabel:
text: "You're in digigold screen"
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'screen-dash'
I found somewhere something related to binding a callback function but I didn't understand that. How do I create such a function and place it inside the toolbar property of MDToolbar in .kv file?
You can just include the change in your on_press for the Button that changes the Screen. Like this:
<DashboardScreen>:
name: 'screen-dash'
MDRectangleFlatButton:
text: "You're in dashboard screen"
pos_hint: {'center_x':0.5,'center_y':0.6}
on_press:
root.manager.current = 'screen-dgld'
app.root.ids.toolbar.title = 'Digital Gold'
<DigitalGoldScreen>:
name: 'screen-dgld'
MDLabel:
text: "You're in digigold screen"
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press:
root.manager.current = 'screen-dash'
app.root.ids.toolbar.title = 'Dashboard'

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)

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

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.

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

Resources