Search code examples
pythonkivy

Kivy - rebuild class/ Boxlayout with updated content


In my Kivy-App, i generate Buttons via a python-class based on a dictionary (in the following example i use a list but that's just an example for the underlying problem). Within the App, the dictionary gets changed and i want to display that change (obviously) in my App (by adding/ removing/ rearranging the Buttons). To achieve this, my approach is to either restart the entire App or only reload that particular BoxLayout. Unfortunately, non of my attempts worked out so far and i could not find any (working) solution on the internet.

This is my code example:

Python Code:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

buttonlist = []
counter = 0

class MainWindow(BoxLayout):
    def addbutton(self):
        global buttonlist
        global counter
        buttonlist.append(counter)
        counter += 1

class ButtonBox(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.orientation = "vertical"
        global buttonlist
        for button in buttonlist:
            b = Button(text=str(button))
            self.add_widget(b)

class KivyApp(App):
    def build(self):
        return MainWindow()

KivyApp().run()

KV Code:

<MainWindow>:
    BoxLayout:
        ButtonBox:
        Button:
            text: "add Button"
            on_press: root.addbutton()

My closest attempt was something containing a restart-Method like:

def restart(self):
    self.stop()
    return KivyApp().run()

and calling:

App.get_running_app().restart()

But for some reason, this does not stop the App but opens a second instance of the App within the first one (resulting in App in App in App in App if pressed often)


Solution

  • You can rebuild the ButtonBox by first calling clear_widgets() on the ButtonBox instance. Here is a modified version of your code that does that:

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.button import Button
    
    kv = '''
    <MainWindow>:
        BoxLayout:
            ButtonBox:
                id: box
            Button:
                text: "add Button"
                on_press: root.addbutton()
    '''
    
    buttonlist = ['Abba', 'Dabba', 'Doo']
    counter = 3
    
    class MainWindow(BoxLayout):
        def addbutton(self):
            global buttonlist
            global counter
            buttonlist.append(str(counter))
            counter += 1
            self.ids.box.reload()
    
    class ButtonBox(BoxLayout):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.orientation = "vertical"
            self.reload()
    
        def reload(self):
            # method to rebuild the ButtonBox contents
            global buttonlist
            self.clear_widgets()
            for button in buttonlist:
                b = Button(text=str(button))
                self.add_widget(b)
    
    class KivyApp(App):
        def build(self):
            Builder.load_string(kv)
            return MainWindow()
    
    KivyApp().run()
    

    I used your kv as a string, just for my own convenience.