Agenobarb
Agenobarb

Reputation: 153

Python Kivy: Dynamic addition of widgets to a screen

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

Answers (1)

PalimPalim
PalimPalim

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

Related Questions