When going from the first screen to the second screen, I want to pass a variable as an argument so that kivyMD can update the second screen from text stored in an excel file. The following is a skeleton of my app's functionality:
The user reaches Screen 1 thru the navigation drawer in KivyMD, screen 1 presents the user with two options on two small clickable MDCards:
"Change text to 1"
"Change text to 2"
After clicking on one of these, the app switches to screen 2 with a single big MDCard, the text on this MDCard should change to reflect the option the user chose.
However, kivy is pulling the text that is to be displayed on the big MDCard from an excel file.
The variable that I want to pass from screen 1 to screen 2 is simply a number (1 or 2) that will tell kivy which row in the excel file it should pull the text from
If the user clicks "Change text to 1" then the first screen should pass "1" as the argument row_x to the function def change_text() (see screen 2 .py) so that the text in row 1 of excel can be displayed on the second screen. How can I achieve this?
I have 4 files in total; 3 are .py files (one for the main app, one for screen 1, and one for screen 2), and the excel file
NOTE: in the code below, Screen 1 & 2 are called Element 1 & 2 respectfully
Main.py:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
from element_1 import element_1_screen
from element_2 import element_2_screen
MainNavigation = '''
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: 'Go to Element 1'
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "go_to_element_1_screen"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "words_nav_item"
element_1_screen:
name: "go_to_element_1_screen"
element_2_screen:
name: "go_to_element_2_screen"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class mainApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Red"
return Builder.load_string(MainNavigation)
mainApp().run()
Screen 1 / Element 1
from kivy.lang import Builder
from kivymd.uix.screen import MDScreen
element_1_contents = '''
<element_1_screen>:
MDGridLayout:
rows: 2
size: root.width, root.height
pos_hint: {"center_x": .8, "center_y": .2}
spacing: 40
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "120dp"
ripple_behavior: True
on_release:
root.manager.current = "go_to_element_2_screen"
MDLabel:
id: LabelTextID
text: "Change Text to 1"
halign: 'center'
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "120dp"
ripple_behavior: True
on_release:
root.manager.current = "go_to_element_2_screen"
MDLabel:
id: LabelTextID
text: "Change Text to 2"
halign: 'center'
'''
class element_1_screen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Builder.load_string(element_1_contents)
Screen 2 / Element 2
from kivy.lang import Builder
from kivymd.uix.screen import MDScreen
import openpyxl
element_2_contents = '''
<element_2_screen>:
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "360dp"
pos_hint: {"center_x": .5, "center_y": .5}
ripple_behavior: True
focus_behavior: True
on_release: root.manager.current = "go_to_element_1_screen"
MDLabel:
id: TextID
text: "NOTHING HAS CHANGED"
halign: 'center'
MDLabel:
text: "(Click here to return)"
halign: 'center'
'''
class element_2_screen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
path = "data.xlsx"
self.wb_obj = openpyxl.load_workbook(path)
self.sheet_obj = self.wb_obj.active
Builder.load_string(element_2_contents)
def change_text(self, row_x=0):
row_number = self.sheet_obj.cell(row_x, column=1)
self.ids.TextID.text = str(row_number.value)
And the excel file only has two entries in Column A:
Row 1: You have chosen 1
Row 2: You have chosen 2
I found the answer and now it works flawlessly. Someone over on Reddit (u/Username_RANDINT) helped me, this is what they said:
The ScreenManager has a get_screen() method. You could use it to get
the instance of the second screen and call the change_text() method on
that. In the same place where you switch screens, add another line:
on_release:
root.manager.current = "go_to_element_2_screen"
root.manager.get_screen("go_to_element_2_screen").change_text(1)
Then the same for the other card, just pass in 2 instead of 1.
Related
I currently have a code that runs a dropdown button in kivy. I would like to send the value 1,2,3,4 into the main class when the value is selected.
Specifying chain of events:
click dropdown button
four options are displayed
select one of the four options ==> this is when the value gets sent back to the main class
dropdown button text replaced with the selection
I have tried using get_screen(), which is equivalent to get_running_app when using windows, but this was delayed. I also tried creating an external variable but this was delayed too.
Below is my code. Thanks.
'''
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.dropdown import DropDown
class WindowManager(ScreenManager):
pass
class Drop_down(DropDown):
pass
class MenuWindow(Screen):
General_select_button_size = (100, 100)
General_select_button_pos = (400, 400)
class GeneralWindow(Screen):
drop_size = (100,100)
drop_pos = (400,400)
xax=''
def on_touch_up(self,touch):
app = self.manager.get_screen('General')
print('use get',app.ids['btn'].text)
print('use variable',self.xax)
KV = Builder.load_string("""
WindowManager:
MenuWindow:
GeneralWindow:
<MenuWIndow>:
name: 'Menu'
RelativeLayout:
Button:
allow_stretch: True
keep_ratio: True
size_hint: None,None
id: general_select_button
text: 'move to general mode'
size: root.General_select_button_size
pos: root.General_select_button_pos
on_release:
app.root.current = "General"
root.manager.transition.direction = "left"
<GeneralWindow>:
name: 'General'
RelativeLayout:
Button:
allow_stretch: True
keep_ratio: True
size_hint: None,None
id: btn
text: 'select number'
size: root.drop_size
pos: root.drop_pos
on_parent: drop_content.dismiss()
on_release:
drop_content.open(self)
root.xax=btn.text
DropDown:
id: drop_content
on_select: btn.text = '{}'.format(args[1])
Button:
id: btn1
text: '1'
size_hint_y: None
height: 30
on_release: drop_content.select('1')
Button:
id: btn2
text:'2'
size_hint_y: None
height: 30
on_release: drop_content.select('2')
Button:
id: btn3
text: '3'
size_hint_y: None
height: 30
on_release: drop_content.select('3')
Button:
id: btn4
text: '4'
size_hint_y: None
height: 30
on_release: drop_content.select('4')
""")
class ImageChangeApp(App):
def build(self):
return KV
if __name__ == '__main__':
ImageChangeApp().run()
'''
I have created code with 2 screens and need one of them to have an expansion panel.
Unfortunately I can't get the panel to show up with the content inside of it. Instead I'm stuck with chaos in my head and a side of migraine, so here is my code, an example of what I want it to look like and what I managed to create minus my full code.
Video example: https://www.kapwing.com/videos/62f4074bafd00100c829b84c
Video example of problem: https://www.kapwing.com/videos/62f41c828f6acd00521caae1
As shown in the video example:
1st code:
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivymd.uix.expansionpanel import MDExpansionPanel
from kivymd.uix.expansionpanel import MDExpansionPanelOneLine
from kivymd.uix.boxlayout import MDBoxLayout
KV = '''
MDScreen:
MDNavigationLayout:
ScreenManager:
id: manager
MDScreen:
name: 'Home'
AnchorLayout:
anchor_x: "center"
anchor_y: "top"
MDToolbar:
md_bg_color: 0, 0, 0, 0.5
title: "Example"
elevation: 10
left_action_items: [["menu", lambda x: mud_list.set_state("open")]]
right_action_items: [["dots-vertical", lambda x:app.dropdown(x)]]
MDNavigationDrawer:
id: mud_list
BoxLayout:
orientation: 'vertical'
spacing: '5dp'
padding: '5dp'
ScrollView:
MDList:
OneLineIconListItem:
text: '[Settings]'
on_release:
manager.current = 'Settings'
root.ids.mud_list.set_state(new_state='toggle', animation=True)
divider: None
IconLeftWidget:
icon: 'cog'
on_release:
manager.current = 'Settings'
root.ids.mud_list.set_state(new_state='toggle', animation=True)
MDLabel:
text:' By Author'
size_hint_y: None
font_style: 'Button'
height: self.texture_size[1]
MDScreen:
name: 'Settings'
AnchorLayout:
anchor_x: "center"
anchor_y: "top"
MDToolbar:
id: mdt_color
md_bg_color: 1, 1, 1, 1
elevation: 10
MDIconButton:
icon: "keyboard-backspace"
pos_hint: {"center_x": 0.09, "center_y": 0.945}
on_release: manager.current = 'Home'
MDBoxLayout:
size_hint: 1, 0.89
orientation : 'vertical'
ScrollView:
MDBoxLayout:
orientation:'vertical'
adaptive_height: True
padding:[dp(15),dp(15),dp(15),dp(35)]
spacing:dp(15)
Content
adaptive_height: True
orientation: 'vertical'
OneLineIconListItem:
text: "Dark"
on_release:app.theme_changer2()
divider: None
IconLeftWidget:
icon: 'weather-night'
on_release:app.theme_changer2()
OneLineIconListItem:
text: "Light"
on_release:app.theme_changer()
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
on_release:app.theme_changer()
ScrollView:
MDGridLayout:
id: box
cols: 1
adaptive_height: True
'''
class Content(MDBoxLayout):
"""Custom content."""
def __draw_shadow__(self, origin, end, context=None):
pass
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.menu = None
self.menu_list = None
self.kvs = Builder.load_string(KV)
self.screen = Builder.load_string(KV)
def on_start(self):
self.root.ids.box.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
content=Content(),
panel_cls=MDExpansionPanelOneLine(
text="Theme",
)
)
)
def theme_changer(self):
self.theme_cls.theme_style = "Light"
self.root.ids.mdt_color.md_bg_color = [1, 1, 1, 1]
def theme_changer2(self):
self.theme_cls.theme_style = "Dark"
self.root.ids.mdt_color.md_bg_color = [0, 0, 0, 1]
def build(self):
self.theme_cls.theme_style = "Light"
screen = Screen()
screen.add_widget(self.kvs)
return self.screen
ma = MainApp()
ma.run()
2nd code: I got from the kivymd documentation here https://github.com/kivymd/KivyMD/wiki/Components-Expansion-Panel
3rd code is much the same as the 2nd but I made my own:
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelOneLine
KV = '''
<Content>
adaptive_height: True
orientation: 'vertical'
OneLineIconListItem:
text: "Dark"
divider: None
IconLeftWidget:
icon: 'weather-night'
OneLineIconListItem:
text: "Light"
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
ScrollView:
MDGridLayout:
id: box
cols: 1
adaptive_height: True
'''
class Content(MDBoxLayout):
"""Custom content."""
def __draw_shadow__(self, origin, end, context=None):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
self.root.ids.box.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
content=Content(),
panel_cls=MDExpansionPanelOneLine(
text="Theme",
)
)
)
Test().run()
My problem is, as seen in the example of problem video, that the expansion panel itself doesn't show up.
I am figuring this out as I go along, so among all the chaos I have tried, I have noticed that the position of the "Content" and all that's under it in relation the anchor layout of screen 'settings', causes the panel to show up but the content isn't inside.
Same effect with whether the "content" or "MDGridlayout" have the id: box.
In summary, I want to be able to create something like in the 2nd code but in the settings screen of my main app, or basically copy and paste the 3rd code into my main app.
Oh, and I may make this a question on it's own later, but if it's simple enough, how can I make it so when the theme is changed it becomes the default?
Took a while, but with help i finally got it. Here's an example with what i think are some key notes in the code #Comments#.
Hope this helps someone.
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelOneLine
KV = '''
#THE CONTENT GOES ABOVE EVERYTHING ELSE EVEN THE 1ST SCREEN.#
#IT IS REFERENCED LATER IN WHICH EVER SCREEN IT IS TO APPEAR USING MDGridLayout AND A def LATER ON#
#It should also always have these angle brackets <>#
<Content>
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
OneLineIconListItem:
text: 'Dark theme'
on_release:app.theme_changer2()
divider: None
IconLeftWidget:
icon: 'weather-night'
on_release:app.theme_changer2()
OneLineIconListItem:
text: 'Light theme'
on_release:app.theme_changer()
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
on_release:app.theme_changer()
<Content2>
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
OneLineIconListItem:
text: 'I try to explain what i think are key points to note'
on_release:app.theme_changer2()
divider: None
IconLeftWidget:
icon: 'weather-night'
on_release:app.theme_changer2()
OneLineIconListItem:
text: 'Hope this helps someone else'
on_release:app.theme_changer()
divider: None
IconLeftWidget:
icon: 'white-balance-sunny'
on_release:app.theme_changer()
MDScreen:
MDNavigationLayout:
ScreenManager:
id: manager
MDScreen:
name: 'Home'
#The location of this MDGridLayout shows it appears in the 1st screen.#
MDGridLayout:
cols: 1
adaptive_height: True
#This id is used to reference what content goes to which expansion panel#
id: box2
MDRaisedButton:
pos_hint: {"center_x": 0.5, "center_y": 0.5}
text: "PRESS ME"
on_release: manager.current = 'Home2'
MDScreen:
name: 'Home2'
MDBoxLayout:
orientation: "vertical"
MDToolbar:
id: mdt_color
elevation: 10
ScrollView:
divider: 'None'
#The location of this MDGridLayout shows it appears in the 2nd screen.#
MDGridLayout:
cols: 1
adaptive_height: True
id: box
MDIconButton:
pos_hint: {"center_x": 0.05, "center_y": 0.945}
icon: "keyboard-backspace"
on_release: manager.current = 'Home'
'''
# Every expansion panel should have a class corresponding to it's name e.g.#
class Content(MDBoxLayout):
pass
# The class name is referenced in the contents= section below#
class Content2(MDBoxLayout):
pass
class MainApp(MDApp):
def theme_changer(self):
self.theme_cls.theme_style = "Light"
self.root.ids.mdt_color.md_bg_color = [1, 1, 1, 1]
def theme_changer2(self):
self.theme_cls.theme_style = "Dark"
self.root.ids.mdt_color.md_bg_color = [0, 0, 0, 1]
def build(self):
return Builder.load_string(KV)
def on_start(self):
# Here you see the id box used#
self.root.ids.box.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
# Here the content name Content is referenced in content=#
content=Content(),
panel_cls=MDExpansionPanelOneLine(
text="Theme",
)
)
)
# Here you see the id box2 used#
self.root.ids.box2.add_widget(
MDExpansionPanel(
icon="theme-light-dark",
# Here the content name Content2 is referenced in content=#
content=Content2(),
panel_cls=MDExpansionPanelOneLine(
text="Read the code #comments# for details",
)
)
)
ma = MainApp()
ma.run()
I have a kivymd app that has a screen with a button on it. When you click this button an MDCard will appear on the screen. When you click on this new MDCard it will call a function that will print a message on the terminal. However, I am having trouble getting this MDCard to call the function. I am getting the error:
AttributeError: 'MDCard' object has no attribute 'option'
The MDCard is in a separate kv string from the main kv string. Essentially I have two kv strings. When you press the button, the second kv string will be added as a widget to the first kv string.
I figured it is because the second kv string doesn't have a class as a root but I don't know how to do this. How can I get the MDCard to call the function??
MAIN.PY
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
from button_screen import button_screen
MainNav = '''
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: 'Go to Button Screen'
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "go_to_button_screen"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "words_nav_item"
button_screen:
name: "go_to_button_screen"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class main_test(MDApp):
def build(self):
self.theme_cls.primary_palette = "Red"
return Builder.load_string(MainNav)
main_test().run()
BUTTON SCREEN .PY FILE
from kivy.lang import Builder
from kivymd.uix.screen import MDScreen
button_screen_kv = '''
<button_screen>:
MDGridLayout:
cols: 1
size: root.width, root.height
spacing: 40
md_bg_color: [0,0,.1,.1]
MDGridLayout:
cols: 1
size_hint_y: None
height: 40
MDGridLayout:
cols: 1
Button:
text: "Click to add card"
on_release:
root.add_card("card 1")
MDGridLayout:
id: add_card_here_id
cols: 1
'''
md_card_kv = '''
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "120dp"
ripple_behavior: True
on_release:
root.option("MDCard was clicked")
MDLabel:
id: LabelTextID
text: "this is an MDCard"
halign: 'center'
'''
class button_screen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Builder.load_string(button_screen_kv)
self.md_card_widget = Builder.load_string(md_card_kv)
def option(self, string):
print(f"{string}")
def add_card(self, *args):
self.ids.add_card_here_id.add_widget(self.md_card_widget)
I came up with the following that works however, I am still new to programming so I don't know if this is a stable solution. Please advise me there
The Solution:
I added another class class user_card(MDGridLayout) in the buttonscreen.py and placed the kv string there.
I set that class as the root widget in the kv string
in the init method I placed a function that will return the kv string return self.user_card_string
then I added that class as a parameter in the add_widget and passed the col as an argument: self.ids.add_card_here_id.add_widget(user_card(cols=1))
Everything works but I have never used two classes before. So I am unsure if this will present a future problem.
button_screen.py:
from kivy.lang import Builder
from kivymd.uix.gridlayout import MDGridLayout
from kivymd.uix.screen import MDScreen
button_screen_kv = '''
<button_screen>:
MDGridLayout:
cols: 1
size: root.width, root.height
spacing: 40
md_bg_color: [0,0,.1,.1]
MDGridLayout:
cols: 1
size_hint_y: None
height: 40
MDGridLayout:
cols: 1
Button:
text: "Click to add card"
on_release:
root.add_card("card 1")
MDGridLayout:
id: add_card_here_id
cols: 1
'''
md_card_kv = '''
<user_card>:
MDGridLayout:
cols: 1
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "120dp"
ripple_behavior: True
on_release:
root.option()
MDLabel:
id: LabelTextID
text: "this is an MDCard"
halign: 'center'
'''
class user_card(MDGridLayout):
user_card_string = Builder.load_string(md_card_kv)
def __init__(self, *args, **kwargs):
super().__init__(**kwargs)
self.load_card()
def option(self, *args):
print("MDCard was clicked")
def load_card(self):
return self.user_card_string
class button_screen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Builder.load_string(button_screen_kv)
def add_card(self, *args):
self.ids.add_card_here_id.add_widget(user_card(cols=1))
I am trying to navigate through my app, I can get the screens to change thru the navigation drawer, however when I am within one of the screens away from the navigation drawer, I don't know how to change to another screen.
The app below allows the user to click on the "Go to Element 1" button in the navigation drawer which will take them to "Element 1 Screen", this screen is away from the navigation drawer and it has a clickable MDCard.
When the user clicks on that MDCard, it should take them to "Element 2 Screen" but that's where I am stuck. I don't know how to get kivy to change the screen here. How can I get the MdCard in element 1 to switch the screen to element 2?
The app is arranged into 3 .py files:
1 for the main app
1 for element_1
1 for element_2
The flow of the code below goes like this:
Main App --> Element 1 button --> Element 1 screen --> Element 2 screen
PS: The code below is only for training purposes, it represents the general layout of my actual app.
Main App Code:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
from element_1 import element_1_screen
from element_2 import element_2_screen
MainNavigation = '''
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: 'Go to Element 1'
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "go_to_element_1_screen"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "words_nav_item"
element_1_screen:
name: "go_to_element_1_screen"
element_2_screen:
name: "go_to_element_2_screen"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class mainApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Red"
return Builder.load_string(MainNavigation)
mainApp().run()
Element 1 Screen:
from kivy.lang import Builder
from kivymd.uix.screen import Screen
element_1_contents = '''
MDScreen:
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "360dp"
pos_hint: {"center_x": .5, "center_y": .5}
ripple_behavior: True
focus_behavior: True
on_press: root.screen_manager.current = "go_to_element_2_screen"
MDLabel:
text: "Welcome to Element 1"
halign: 'center'
MDLabel:
text: "Click here to go to element 2"
halign: 'center'
'''
class element_1_screen(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.directory = Builder.load_string(element_1_contents)
self.add_widget(self.directory)
Element 2 Screen:
# Element 2 has the same code as element 1 except that for these two lines
MDLabel:
text: "Welcome to Element 2"
MDLabel:
text: "Click here to go to element 3"
One hack to get it working is to change:
root.screen_manager.current =
to:
root.parent.manager.current =
in the element_1.py and element_2.py files.
I have a problem with my Kivy Python Code. I have 2 screens: 1st is to navigate to the second screen and on the 2nd screen there is a button to add text to a scrollview...navigating is working but it does not add any text to the scrollview...I think I need some help here! AttributeError: 'super' object has no attribute 'getattr'
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock, mainthread
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
from kivy.effects.scroll import ScrollEffect
from kivy.lang import Builder
Builder.load_string("""
<MenuScreen>:
name: 'mainmenu'
BoxLayout:
spacing: 1
orientation: "vertical"
Label:
text: "MAIN MENU"
Button:
text: 'Go to Screen 2'
on_release:
root.manager.current = 'screen2'
root.manager.transition.direction = "left"
Button:
text: 'Quit'
on_release: root.manager.current = app.exit_software()
<Screen2>:
name: 'screen2'
BoxLayout:
spacing: 1
orientation: "vertical"
ScrollView:
id: scroll_view
always_overscroll: False
BoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Label:
id: label
text: "You can add some Text here by pressing the button"
size_hint: None, None
size: self.texture_size
Button:
text: 'Add text!'
size_hint_y: 0.1
on_release: app.add_text()
Button:
text: 'Back to main menu'
size_hint_y: 0.1
on_release:
root.manager.current = 'mainmenu'
root.manager.transition.direction = "right"
""")
# Declare both screens
class MenuScreen(Screen):
pass
class Screen2(Screen):
pass
class AddTextApp(App):
def __init__(self,**kwargs):
super().__init__(**kwargs)
def build(self):
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='mainmenu'))
sm.add_widget(Screen2(name='screen2'))
return sm
def add_text(self):
self.root.ids.label.text += f"Some new Text\n"
self.root.ids.scroll_view.scroll_y = 0
def exit_software(self):
App.get_running_app().stop()
if __name__ == '__main__':
AddTextApp().run()
Thank you very much in advance!
The error occurred because self.root.ids gets access to widgets located in the root widget of the main class. To access the secondary screen elements, you need to add it to the main class (in your case, in ScreenManager) and set its id. Also, you have a lot of imported excess, so that it is clearly visible, I advise you to use Pycharm or something like that.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
kv = """
<MenuScreen>:
name: 'mainmenu'
BoxLayout:
spacing: 1
orientation: "vertical"
Label:
text: "MAIN MENU"
Button:
text: 'Go to Screen 2'
on_release:
root.manager.current = 'screen2'
root.manager.transition.direction = "left"
Button:
text: 'Quit'
on_release: root.manager.current = app.exit_software()
<Screen2>:
name: 'screen2'
BoxLayout:
spacing: 1
orientation: "vertical"
ScrollView:
id: scroll_view
always_overscroll: False
BoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Label:
id: label
text: "You can add some Text here by pressing the button"
size_hint: None, None
size: self.texture_size
Button:
text: 'Add text!'
size_hint_y: 0.1
on_release: app.add_text()
Button:
text: 'Back to main menu'
size_hint_y: 0.1
on_release:
root.manager.current = 'mainmenu'
root.manager.transition.direction = "right"
ScreenManager:
MenuScreen:
id: menu_scr
Screen2:
id: scr_2
"""
class MenuScreen(Screen):
pass
class Screen2(Screen):
pass
class AddTextApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
return Builder.load_string(kv)
def add_text(self):
self.root.ids.scr_2.ids.label.text += f"Some new Text\n"
self.root.ids.scr_2.ids.scroll_view.scroll_y = 0
#staticmethod
def exit_software():
App.get_running_app().stop()
if __name__ == '__main__':
AddTextApp().run()