How to switch between screens within the NavigationDrawer using KivyMD - python

I'm beginner in using kivymd and trying to switch between screens within the NavigationDrawer using KivyMD, I was reading some of the documents and ended up with this code but it's not working with me:
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.theming import ThemableBehavior
from kivymd.uix.list import MDList
from kivymd.app import MDApp
Window.size = (300, 500)
navigation_helper = """
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "MDNavigationDrawer"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
NavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "scr 1"
MDLabel:
text: "Go to Hell"
halign: "center"
Screen:
name: "scr 2"
MDLabel:
text: "Hell"
halign: "center"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
ScrollView:
MDList:
OneLineListItem:
text: "Screen 1"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 1"
OneLineListItem:
text: "Screen 2"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 2"
"""
class DemoApp(MDApp):
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class DrawerList(ThemableBehavior, MDList):
pass
def build(self):
screen = Builder.load_string(navigation_helper)
return screen
def on_start(self):
pass
DemoApp().run()
But it's giving me this error " AttributeError: 'Screen' object has no attribute 'nav_drawer'", I'm not sure what I've done incorrect.

When you are referencing an object using an id within the same rule where the id is defined, you do not need to prepend it with root:
OneLineListItem:
text: "Screen 1"
on_press:
nav_drawer.set_state("close")
screen_manager.current = "scr 1"
OneLineListItem:
text: "Screen 2"
on_press:
nav_drawer.set_state("close")
screen_manager.current = "scr 2"

Related

change state of action buttons on the toolbar in KivyMD

I'm trying to write an program for the phone in python using kivy md. The program dynamically adds modules to a page to be counted. I've already got that part sorted in simple kivy.
I'm now trying to replicate it in KivyMD because I can use a navigation drawer and it's screen manager. The program has 2 screens.
Is there way to change the state of the buttons in the action items according to the screen that is active?
So I'm trying to have 2 overlapped buttons on the right side of the toolbar and show button1 when screen 1 is active and hide button2 and vice-versa.
I used the example from KivyMD documentation on the NavDrawer with the Screen manager and built on that. The left action items is used for the navigation drawer and I am trying to add 2 more buttons on the right side an change their state according to the active screen.
I only managed to add one but I don't know how to add the second one and how to change their state.
Please see below the code I am using:
python file
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.app import MDApp
from kivymd.uix.scrollview import MDScrollView
class ContentNavigationDrawer(MDScrollView):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class AbacApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Orange"
self.theme_cls.theme_style = "Dark"
return Builder.load_file('main.kv')
if __name__ == '__main__':
AbacApp().run()
kv file
<ContentNavigationDrawer>
MDBoxLayout:
orientation: "vertical"
MDAnchorLayout:
anchor_y: "top"
MDList:
TwoLineAvatarListItem:
text: "Abacu' lu' Lysu"
secondary_text: "Lisandru numara"
ImageLeftWidget:
source: "img.png"
MDList:
OneLineListItem:
text: "Module"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 1"
OneLineListItem:
text: "Printare"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 2"
MDScreen:
MDBottomAppBar:
MDTopAppBar:
pos_hint: {"bottom": 1}
elevation: 4
icon: "plus"
title: "Abacu' lu' Lisandru"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
on_action_button:
id: add_module
app.callback()
on_action_button:
id: print_modules
app.callback()
mode: "end"
MDNavigationLayout:
MDScreenManager:
id: screen_manager
MDScreen:
name: "scr 1"
MDLabel:
text: "Screen 1"
halign: "center"
MDScreen:
name: "scr 2"
MDLabel:
text: "Screen 2"
halign: "center"
MDNavigationDrawer:
id: nav_drawer
radius: (0, 16, 16, 0)
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer

How do I get KivyMD Navigation Drawer to call another app in a separate .py file?

I know how to write an app with kivy and I understand how to make a KivyMD app that has a navigation drawer. I can make the apps run individually but how do I combine the two?
I am new to coding and I am stuck here. Below are the codes for two mock-up apps for training purposes only. The first is a very simple app called "+5 Game" that allows the user to click a button to add 5 to the total which is then displayed in the GUI.
The second is a KivyMD app with a navigation drawer that contains two buttons. One to go to the home screen and one to go to the "+5 Game"
How do I link the button in the navigation draw to the +5 game?
And what do I do about the imports at the top of the file?
This is the code for the +5 Game:
from kivymd.app import MDApp
from kivymd.uix.screen import Screen
from kivymd.uix.label import MDLabel
from kivymd.uix.button import MDRectangleFlatButton
class ClickToAdd(MDApp):
def build(self):
self.screen = Screen()
self.num = 0
self.display_number = MDLabel(text="0", font_style="H1", halign="center",
pos_hint={'center_x': 0.5, 'center_y': 0.8})
self.add5_BTN = MDRectangleFlatButton(text="+5", pos_hint={'center_x': 0.5, 'center_y': 0.65},
on_release=self.add5_CMD)
self.screen.add_widget(self.display_number)
self.screen.add_widget(self.add5_BTN)
return self.screen
def add5_CMD(self, *args):
self.num = self.num + 5
self.display_number.text = str(self.num)
ClickToAdd().run()
This is the main GUI of the KivyMD app with a navigation drawer and a screen manager:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
KV = '''
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: "Home"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "home"
OneLineListItem:
text: "Play +5 Game"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "plus5game"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "Navigation Drawer Test"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "home"
MDLabel:
text: "Home Screen Stuff Goes Here"
halign: "center"
Screen:
name: "plus5game"
MDLabel:
text: "+5 Game Goes here"
halign: "center"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class TestNavigationDrawer(MDApp):
def build(self):
return Builder.load_string(KV)
TestNavigationDrawer().run()
You cannot run one App inside another App, but you can use the guts of another App inside the first. In your case, you can redefine the the Plus5 App like this:
from kivymd.app import MDApp
from kivymd.uix.screen import Screen
from kivymd.uix.label import MDLabel
from kivymd.uix.button import MDRectangleFlatButton
class Plus5Screen(Screen):
def __init__(self, **kwargs):
super(Plus5Screen, self).__init__(**kwargs)
self.num = 0
self.display_number = MDLabel(text="0", font_style="H1", halign="center",
pos_hint={'center_x': 0.5, 'center_y': 0.8})
self.add5_BTN = MDRectangleFlatButton(text="+5", pos_hint={'center_x': 0.5, 'center_y': 0.65},
on_release=self.add5_CMD)
self.add_widget(self.display_number)
self.add_widget(self.add5_BTN)
def add5_CMD(self, *args):
self.num = self.num + 5
self.display_number.text = str(self.num)
class ClickToAdd(MDApp):
def build(self):
self.screen = Plus5Screen()
return self.screen
if __name__=='__main__':
ClickToAdd().run()
and save this is a file named plus5.py.
Then in you main App, you can just reference this in the 'kv':
KV = '''
#:import Plus5Screen plus5 # import the guts of the Plus5 game
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: "Home"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "home"
OneLineListItem:
text: "Play +5 Game"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "plus5game"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "Navigation Drawer Test"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "home"
MDLabel:
text: "Home Screen Stuff Goes Here"
halign: "center"
Plus5Screen: # this creates the guts of the Plu5 game
name: "plus5game"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''

How to combine Navigation Drawer and multiple Screens in KivyMD (Python)?

I'm a coding beginner and I want to make a mobile app which contains a Toolbar with a Navigation Drawer and that one should bring the user to different screens in the app. I searched a lot around for a solution, but didn't really found one, because sometimes the whole code is in the .kv file, so it is hard to handle variables and print them for example such as in this code:
How to switch between screens within the NavigationDrawer using KivyMD
I tried to combine it with this code which uses only Kivy instead of KivyMD but I failed bacause it isn't really for mobile Apps:
https://www.techwithtim.net/tutorials/kivy-tutorial/multiple-screens/
I hope someone can help me.
Thx!
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
KV = '''
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: "Screen 1"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 1"
OneLineListItem:
text: "Screen 2"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 2"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "MDNavigationDrawer"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "scr 1"
MDLabel:
text: "Screen 1"
halign: "center"
Screen:
name: "scr 2"
MDLabel:
text: "Screen 2"
halign: "center"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class TestNavigationDrawer(MDApp):
def build(self):
return Builder.load_string(KV)
TestNavigationDrawer().run()
https://kivymd.readthedocs.io/en/latest/components/navigation-drawer/#switching-screens-in-the-screenmanager-and-using-the-common-mdtoolbar

KivyMd screen does not change

I have this python file
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.theming import ThemableBehavior, ThemeManager
from kivymd.uix.list import OneLineIconListItem, MDList
class ContentNavigationDrawer(BoxLayout):
pass
class ItemDrawer(OneLineIconListItem):
icon = StringProperty()
class MainApp(MDApp):
def on_start(self):
icons_item = {
'bug': 'Files'
}
for icon_name in icons_item.keys():
self.root.ids.content_drawer.ids.md_list.add_widget(
ItemDrawer(icon=icon_name, text=icons_item[icon_name])
)
class DrawerList(ThemableBehavior, MDList):
def set_color_item(self, instance_item):
# Set the color of the icon and text for the menu item.
for item in self.children:
if item.text_color == self.theme_cls.primary_color:
item.text_color = self.theme_cls.text_color
break
instance_item.text_color = self.theme_cls.primary_color
MainApp().run()
And this .kv file
See Line 3, the error, comes there that screen_manager is not defined
<ItemDrawer>:
theme_text_color: "Custom"
on_release: screen_manager.current = 'screen2'
IconLeftWidget:
id: icon
icon: root.icon
theme_text_color: "Custom"
text_color: 0,0,0,1
<ContentNavigationDrawer>:
orientation: "vertical"
padding: "8dp"
spacing: "8dp"
AnchorLayout:
anchor_x: "left"
size_hint_y: None
height: avatar.height
Image:
id: avatar
size_hint: None, None
size: "250", "250"
source: "logo.jpg"
ScrollView:
DrawerList:
id: md_list
NavigationLayout:
ScreenManager:
id: screen_manager
Screen:
name: 'screen1'
BoxLayout:
orientation: 'vertical'
Widget:
canvas:
Color:
rgba: 0,1,0.2117647058823529,0.3
Rectangle:
size: self.size
pos: self.pos
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
id: content_drawer
Screen:
name: 'screen2'
When I click on the item in the navigation drawer, it says that screen_manager is not defined
What am I doing wrong? I searched the internet a lot but couldnot find anything useful
Help me please, how do i change screens?
The MDNavigation Drawer should be at the same indent level as the ScreenManager. Furthermore,
MDNavigationLayout:
screen_manager:screen_manager
ScreenManager:
id: screen_manager
Screen:
name: 'screen1'
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: "Navigation Drawer"
elevation: 10
left_action_items: [['menu', lambda x:nav_drawer.set_state("open")]]
Widget:
Screen:
name: 'screen2'
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
id: content_drawer
add the
screen_manager:screen_manager
beneath the NavigationLayout so that it can be accessed as an ObjectProperty. Finally change the ItemDrawer callback from
on_release: screen_manager.current = 'screen2'
to
on_release: app.root.screen_manager.current = 'screen2'
Finally you can also add
nav_drawer: nav_drawer
beneath your root window and just ontop/below the line screen_manager: screen_manager
This enables for dismissing of the navigation drawer when the new screen pops up.
then update your callback to this:
on_release:
app.root.screen_manager.current = 'screen2'
app.root.nav_drawer.set_state('close')

KivyMD: Accessing Label ID From Screen Class

I am using KivyMD and would like to refrash data in the the secound screen using the "on_pre_enter" method.
I am trying to access a label id from screen class "on_pre_enter" method with out success.
I am able to access the label id from the "MainApp" class but unable to access it from the "Second_Screen" class.
MainApp.py
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager, Screen
class MainApp(MDApp):
def __init__(self, **kwargs):
self.title = "My Material Application"
self.theme_cls.primary_palette = "Blue"
super().__init__(**kwargs)
def on_start(self):
self.root.ids.main_screen_label.text = "Main Screen Updated"
def build(self):
self.root = Builder.load_file("app.kv")
class Second_Screen(Screen):
def on_pre_enter(self):
MainApp.ids.second_screen_label = "Second Screen Updated"
pass
if __name__ == "__main__":
MainApp().run()
App.kv
NavigationLayout:
MDNavigationDrawer:
NavigationDrawerSubheader:
text: "Menu:"
NavigationDrawerIconButton:
icon:'battery'
text: "Main Screen"
on_release:
screen_manager.current = "main_screen"
NavigationDrawerIconButton:
icon:'battery'
text: "Second Screen"
on_release:
screen_manager.current = "second_screen"
BoxLayout:
id: box1
orientation: 'vertical'
MDToolbar:
title: "My App"
md_bg_color: app.theme_cls.primary_color
left_action_items:
[['menu', lambda x: app.root.toggle_nav_drawer()]]
ScreenManager:
id: screen_manager
Screen:
name: "main_screen"
BoxLayout:
size_hint: .8, .8
pos_hint: {"center_x": .5, "center_y": .5}
spacing: dp(100)
orientation: 'vertical'
MDLabel:
id: main_screen_label
text: "Main Screen Default"
Second_Screen:
name: "second_screen"
FloatLayout:
id: float
size_hint: .8, .8
pos_hint: {"center_x": .5, "center_y": .5}
spacing: dp(100)
orientation: 'vertical'
MDLabel:
id: second_screen_label
text: "Second Screen Default"
I also tried using:
class Second_Screen(Screen):
def on_pre_enter(self):
self.ids.second_screen_label.text = "Second Screen Updated"
pass
After starting the i get the following error:
self.ids.second_screen_label.text = "Second Screen Updated"
File "kivy\properties.pyx", line 863, in
kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
What is the correct way to access the id's defined in the KV file from the screen class?
Define screen layout in separate rule <MyScreen>:
<MyScreen>:
name: "my_screen"
FloatLayout:
# ...
To add this screen to ScreenManager, just write:
ScreenManager:
id: screen_manager
MyScreen:
To get access to screen widgets use
from screen class: self.ids.my_id
from app class: self.root.ids.screen_manager.get_screen("screen_name").ids.my_id
from other widgets: App.get_running_app()..root.ids.screen_manager.get_screen("screen_name").ids.my_id
So your full code will be:
main.py
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
class MainApp(MDApp):
def __init__(self, **kwargs):
self.title = "My Material Application"
self.theme_cls.primary_palette = "Blue"
super().__init__(**kwargs)
def build(self):
self.root = Builder.load_file("app.kv")
def on_start(self):
self.root.ids.screen_manager.get_screen(
"main_screen"
).ids.main_screen_label.text = "Main Screen Updated"
class MainScreen(Screen):
pass
class SecondScreen(Screen):
def on_pre_enter(self):
self.ids.second_screen_label.text = "Second Screen Updated"
if __name__ == "__main__":
MainApp().run()
app.kv
NavigationLayout:
MDNavigationDrawer:
NavigationDrawerSubheader:
text: "Menu:"
NavigationDrawerIconButton:
icon: "battery"
text: "Main Screen"
on_release:
screen_manager.current = "main_screen"
NavigationDrawerIconButton:
icon: "battery"
text: "Second Screen"
on_release:
screen_manager.current = "second_screen"
BoxLayout:
id: box1
orientation: "vertical"
MDToolbar:
title: "My App"
md_bg_color: app.theme_cls.primary_color
left_action_items:
[["menu", lambda x: app.root.toggle_nav_drawer()]]
ScreenManager:
id: screen_manager
MainScreen:
SecondScreen:
<MainScreen>:
name: "main_screen"
BoxLayout:
size_hint: .8, .8
pos_hint: {"center_x": .5, "center_y": .5}
spacing: dp(100)
orientation: "vertical"
MDLabel:
id: main_screen_label
text: "Main Screen Default"
<SecondScreen>:
name: "second_screen"
FloatLayout:
id: float
size_hint: .8, .8
pos_hint: {"center_x": .5, "center_y": .5}
spacing: dp(100)
orientation: "vertical"
MDLabel:
id: second_screen_label
text: "Second Screen Default"

Categories