Open Kivy Settings inside Screen from NavigationDrawer - python

I want to open the kivy settings inside a screen from the kivymd navigationdrawer. The default settings only open in a new window that completely ignore the color theme of the app. Any suggestion on how to make the settings a child of the screen manager?
Here is the guiApp.py:
from kivy.app import App
import kivymd
from kivymd.theming import ThemeManager
from kivy.uix.settings import Settings, SettingsWithSidebar
class guiApp(App):
theme_cls = ThemeManager()
theme_cls.primary_palette = 'BlueGray'
theme_cls.theme_style = 'Light'
def build(self):
self.settings_cls = SettingsWithSidebar
guiApp().run()
and the gui.kv:
NavigationLayout:
MDNavigationDrawer:
NavigationDrawerSubheader:
text: 'Operation Menu'
NavigationDrawerIconButton:
icon: 'information-outline'
text: 'Introduction'
on_release: screen_manager.current = 'screen_info'
NavigationDrawerIconButton:
icon: 'settings'
text: 'Settings'
on_release: screen_manager.current = 'screen_settings'
on_release: app.open_settings()
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'My GUI'
md_bg_color: app.theme_cls.primary_color
left_action_items: [['menu', lambda x: root.toggle_nav_drawer()]]
ScreenManager:
id: screen_manager
Screen:
name: 'screen_info'
MDLabel:
text: 'This page will be used for information on how to use the App '
theme_text_color : 'Hint'
valign: 'middle'
halign: 'center'
Screen:
name: 'screen_settings'
BoxLayout:

Instead of using app.open_settings(), you can use app.create_settings(), to get the setting widget, that you can directly attach to a Screen.
Add the on_start method to guiApp class
[...]
class guiApp(App):
[...]
def on_start(self):
s = self.create_settings()
self.root.ids.settings_content.add_widget(s)
And give an id to the BoxLayout of your screen_settings
[...]
Screen:
name: 'screen_settings'
BoxLayout:
id: settings_content

Related

Why my ScreenManager not switching in Kivy

I'm trying to build an interface in python with Kivy. To do this, i want to follow this structure:
[ScreenManager#1] -> [Login Screen]
[ScreenManager#2] -> [All others screens]
My ScreenManager#1 is declared in my App function in python file. My ScreenManager#2 is initialised in a kv file. To simply the help that you can bring to me, i've joined the two in the code below.
my AllScreensScreenManager#2, need to have a template shared with all the other screens. I've gave an id to it and call this one with my button in my LoginScreen, like this was suggested here. But the switch is not working. What i'm doing wrong with it.
import kivy
kivy.require('2.1.0') # replace with your current kivy version !
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager,Screen
Builder.load_string('''
<AllScreens>:
id: second_manager
manager: scr_manager
orientation: "vertical"
BoxLayout:
ScreenManager:
id: scr_manager
transition: "left"
Welcome:
GenerateScreen:
ModifyScreen:
ConsultScreen:
LogoutScreen:
BoxLayout:
size_hint: 1, None
height: "40dp"
pos_hint: {"bottom": 0}
spacing: "5dp"
Button:
id: consult
text: "Consult"
on_press: root.manager.current("consult")
Button:
id: modify
text: "Modify"
on_press: root.manager.current("modify")
Button:
id: generate
text: "Generate"
on_press: root.manager.current("generate")
Button:
id: logout
text: "Logout"
on_press: root.manager.current("logout")
<LoginScreen>:
name: "login"
BoxLayout:
Label:
text: "Login"
Button:
text: "Connect"
on_release: second_manager.current("welcome")
<WelcomeScreen>:
name: "welcome"
BoxLayout:
Label:
text: "welcome"
<ConsultScreen>:
name: "consult"
BoxLayout:
Label:
text: "consult"
<GenerateScreen>:
name: "generate"
BoxLayout:
Label:
text: "password"
''')
class ScreenManagement(ScreenManager):
pass
class LoginScreen(Screen):
print("login screen")
pass
class WelcomeScreen(Screen):
pass
class AllScreens(ScreenManager):
pass
class ConsultScreen(Screen):
pass
class GenerateScreen(Screen):
pass
class MyApp(App):
def build(self):
sm = ScreenManagement()
sm.add_widget(LoginScreen(name="login"))
return sm
if __name__ == '__main__':
MyApp().run()

How can I change default active item of Bottom Navigation in kivymd?

I am a beginner in kivymd. I was trying to create bottom-navigation in kivymd. After run the code the it showing active item of Bottom Navigation is 'Home'(name='screen_1). Now I want second item as default active item -> 'Features'(name='screen_2'). This is sample code.
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
Window.size=(350,593)
KV_string = """
Screen:
BoxLayout:
orientation:'vertical'
MDToolbar:
title:'Demo Application'
right_action_items : [["dots-vertical"]]
left_action_items : [["menu", lambda x: app.menu_toggle()]]
right_action_items : [["dots-vertical", lambda x: app.option_fun()]]
elevation:10
MDLabel:
text:'Welcome to good GUI'
halign:'center'
MDBottomNavigation:
MDBottomNavigationItem:
name: 'screen_1'
text: 'Home'
icon: 'home-outline'
on_tab_press: app.Bottom_nav_fun()
MDLabel:
text: 'Home page'
halign: 'center'
MDBottomNavigationItem:
name: 'screen_2'
text: 'Features'
icon: 'feature-search-outline'
MDLabel:
text: 'Features of this app'
halign: 'center'
MDBottomNavigationItem:
name: 'screen_3'
text: 'Developers'
icon: 'account-supervisor'
MDLabel:
text: 'Developers details'
halign: 'center'
"""
class MyApp(MDApp):
def build(self):
self.theme_cls.primary_palette="Blue"
self.theme_cls.theme_style="Light"
self.theme_cls.primary_hue="A700"
self.screen = Builder.load_string(KV_string)
return self.screen
def menu_toggle(self):
print("Menu toggle Working")
def option_fun(self):
print("Option method Working")
def Bottom_nav_fun(self):
print("Bottom nav home")
if __name__=='__main__':
MyApp().run()
Is there any way to implement this?
Use method switch_tab which takes as argument the name of the tab you want to switch to - https://kivymd.readthedocs.io/en/latest/components/bottomnavigation/#how-to-automatically-switch-a-tab

AssertionError in the kivymd

I M trying to add screen inside the screen manager but giving error of assertion when assigning navigation drawer in a screen manager.
This is the Error i am facing :
self._apply_rule(
File "/home/hp/.local/lib/python3.8/site-packages/kivy/lang/builder.py", line 559, in _apply_rule
assert(rule not in self.rulectx)
AssertionError
This is my Code:
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
screen_helper = """
ScreenManager:
MenuScreen:
ProfileScreen:
ContentNavigationDrawer:
<MenuScreen>:
name: 'menu'
MDRectangleFlatButton:
text: 'Profile'
pos_hint: {'center_x':0.5,'center_y':0.6}
on_press: root.manager.current = 'profile'
MDRectangleFlatButton:
text: 'Upload'
pos_hint: {'center_x':0.5,'center_y':0.5}
on_press: root.manager.current = 'upload'
<ProfileScreen>:
name: 'profile'
MDLabel:
text: 'Profile'
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
<ContentNavigationDrawer>:
name : 'upload'
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")]]
NavigationLayout:
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 MenuScreen(Screen):
pass
class ProfileScreen(Screen):
pass
class ContentNavigationDrawer(Screen):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(ProfileScreen(name='profile'))
sm.add_widget(ContentNavigationDrawer(name='upload'))
class DemoApp(MDApp):
def build(self):
screen = Builder.load_string(screen_helper)
return screen
DemoApp().run()
I believe the problem is that your rule in screen_helper:
<ContentNavigationDrawer>:
includes:
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
That rule combination tells kivy that whenever it creates a ContentNavigationDrawer it should create another ContentNavigationDrawer as a child of the first. Then it would try to apply the original <ContentNavigationDrawer>: again, creating a third ContentNavigationDrawer, applying the rule again to create a fourth, and so on. This would create an infinite loop. Kivy uses the assertion to avoid such loops. So, you just cannot use a rule that would create such a loop.

KivyMD MDNavigationRail, press icons

I was trying to use a new feature in KivyMD, the MDNavigationRail and wanted to give the icons in it a function. The goal is that the user could change to the desired screen by pressing the icon that represents it. I gave the icon an on_press. But something goes wrong, I get an error; ValueError: MDNavigationRail.state is set to an invalid option 'down'. Must be one of: ['close', 'open']. The rail should be open or closed I guess, isn't it possible to give it a function? Furthermore, I would want to know if it is possible to not break the text. If anyone could help me out, it would be very nice!
My .py file
from kivy.uix.screenmanager import ScreenManager
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
class Screen1(MDScreen):
def screen2(self):
self.manager.current = 'screen2'
class Screen2(MDScreen):
def screen1(self):
self.manager.current = 'screen1'
def rail_open(self):
if self.ids.rail.state == "open":
self.ids.rail.state = "close"
else:
self.ids.rail.state = "open"
class MyScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MyScreenManager, self).__init__(**kwargs)
class Test(MDApp):
def build(self):
return MyScreenManager()
Test().run()
My kv file
<MyScreenManager>:
Screen1:
id: screen1
name: 'screen1'
Screen2:
id: screen2
name: 'screen2'
<Screen1>:
id: screen1
MDFloatLayout:
MDRectangleFlatButton:
text: "Change to screen 2"
on_press: root.screen2()
pos_hint: {'center_x':0.5, 'center_y':0.5}
<Screen2>:
id: screen2
MDBoxLayout:
orientation: "vertical"
MDToolbar:
left_action_items: [["menu", lambda x: root.rail_open()]]
MDBoxLayout:
MDNavigationRail:
id: rail
elevation: 1
use_resizeable: True
MDNavigationRailItem:
icon: "home"
text: "homepage"
on_press: root.screen1()
MDNavigationRailItem:
icon: ""
text: ""
MDFloatLayout:
MDTextField:
id: field1
hint_text: "Enter something:"
size_hint_x: 0.4
pos_hint: {'center_x':0.25,'top':0.8}
It was a bug. Already fixed - https://github.com/kivymd/KivyMD/commit/8a31b0f3ccad9c2d9ad35d80953f7396f2dc78f2

KivyMD: Contents of Screen overlap with Toolbar

I'm writing a simple app in KivyMD. According to the kivy documentation my .kv structure with MDNavigationDrawer and MDToolbar is right and everything works fine as long as the screens are empty. When I add content to them, the content instead of being under the Toolbar is above it. How can I fix it?
Here is my code:
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: "Test"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
NavigationLayout:
x: toolbar.height
ScreenManager:
Screen:
name: "scr 1"
ScrollView:
BoxLayout:
orientation: "vertical"
Button:
text: "Hello"
Button:
text: "I wish"
Button:
text: "I could"
Button:
text: "Finally get"
Button:
text: "This to work"
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()
Output of the following code:
The Screen class is a RelativeLayout, so you must position its children as you would any RelativeLayout. Every child of the Screen gets the default size_hint of (1,1) and the default pos of (0,0), so you must adjust it if that is not what you want. In your case, the NavigationLayout will completely cover the Screen based on those default values. You can fix that by just adding a size_hint_y, like this:
NavigationLayout:
x: toolbar.height
size_hint_y: 1.0 - toolbar.height/root.height
This sets the size of the NavigationLayout so that it just meets the bottom of the MDToolbar instead of overlpping it.
It's the order of appearance that is the problem:
If the screen has other widgets such as cards and Layouts, all of these must appear first.
i.e. put the Navigation Layout and the respective screen manager at the bottom.
What comes last appears on top of everything else.
That worked for me.
A most elegant solution is to insert the MDToolbar and NavigationLayout into a BoxLayout with orientation: "vertical" so the NavigationLayout start at the bottom of the Toolbar, without calculating the relative position.
Screen:
BoxLayout:
orientation: "vertical"
MDToolbar:
MDNavigationLayout:

Categories

Resources