Reputation: 153
What would be a good way to add widgets to a screen from python code (dynamicly)?
My goal is to have two 'main' screens. The content of the second screen depends on what has been selected on the Main screen. This content can be seen for simplicity as a variable number of buttons (all content like number of buttons, background images etc stored in the database and updated independently). So the logic I imagine as follows: when MainScreen button pressed, I go to database and retrieve the data by key 'button N' and show it on the second screen in the form of customized Button/Label widgets . I also want to get back to the MainScreen to select another button in it if needed.
Lets assume I have two screens which static graphics is defined in KV file:
<MainScreen>
BoxLayout:
orientation: "vertical"
Label:
text: "SELECT BUTTON"
Button:
on_press: root.show_buttons(self)
text: "Button 1"
Button:
on_press: root.show_buttons(self)
text: "Button 2"
<AnotherScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text: "Select BUTTON"
Button:
on_press: root.manager.current = 'Main'
text: "BACK"
MainScreen is fully static. When a button is pressed, I need to invoke AnotherScreen and add content to it depending on which button is pressed. I made an attempt to do it as follows (and failed):
class MainScreen(Screen):
def show_buttons(self, btn):
if btn.text == 'Button 1':
another_screen.add_widget(Label(text="Button 1 pressed"))
sm.current = "Another"
return sm
class AnotherScreen(Screen):
pass
class CoolApp(App):
def build(self):
global sm
sm = ScreenManager()
main_screen = MainScreen(name='Main')
sm.add_widget(main_screen)
global another_screen
another_screen = AnotherScreen(name='Another')
sm.add_widget(another_screen)
return sm
if __name__ == '__main__':
CoolApp().run()
In this case, dynamic widget another_screen.add_widget(Label(text="Button pressed"))
is added to a screen which by defintion can have only one child (already defined in KV). I didn't get exception but I got two different widgets rendered at the same time which is equally bad.
I guess I need to add dynamic widgets to BoxLayout in because it is the root widget of that screen. However, I don't know if it is possible to reference widget from different class and if so, is it that appropriate?
Thank you!
Upvotes: 3
Views: 5674
Reputation: 3068
One way is using ids.
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
class MainScreen(Screen):
first_btn_press = True
def show_buttons(self, btn):
if btn.text == 'Button 1':
print(self.manager.ids)
if self.first_btn_press:
self.manager.ids.another.ids.box.add_widget(Label(text="Button 1 pressed"))
self.first_btn_press = False
self.manager.current = "another"
class AnotherScreen(Screen):
pass
kv_str = Builder.load_string("""
ScreenManager:
MainScreen:
id: main
AnotherScreen:
id: another
<MainScreen>
name: "main"
BoxLayout:
orientation: "vertical"
Label:
text: "SELECT BUTTON"
Button:
on_press: root.show_buttons(self)
text: "Button 1"
Button:
on_press: root.show_buttons(self)
text: "Button 2"
<AnotherScreen>:
id: another
name: "another"
BoxLayout:
id: box
orientation: 'vertical'
Label:
text: "Select BUTTON"
Button:
on_press: root.manager.current = 'main'
text: "BACK"
""")
class CoolApp(App):
def build(self):
return kv_str
if __name__ == '__main__':
CoolApp().run()
In the end it is up to you. Another possibility would be using ObjectProperties in the ScreenManager or directly in the app class. The solution above is my preferred way of doing it.
Upvotes: 1