Search code examples
pythonkivykivy-language

Appending and removing code elements within Kivy language on button click


I have written a Kivy GUI that consists of various buttons and nested layouts. I want to be able to, on a click of a button, append a section of code n number of times within one of these layouts, specifically the scroll layout. Since I have am very new to Kivy and since I cannot seem to find a tutorial on this matter, I am presenting it here.

Here is the Python code:

import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.widget import Widget


class Interface(Widget):
    pass

class GUI(App):
    def build(self):
        return Interface()

if __name__ == "__main__":
    GUI().run()


And the total Kivy code:

<Interface>:
    GridLayout:
        padding:0
        size: root.width, root.height
        cols:1
        #Top buttons
        GridLayout:
            size_hint: 1, 0.07
            padding:0
            cols:1
            Button:
                text:"Add Phase"
        #Chart areas
        ScrollView:
            do_scroll_y:False
            BoxLayout:
                orientation: "vertical"
                size_hint_x: None
                width: self.minimum_width
                GridLayout:
                    size_hint_x: None
                    width: self.minimum_width
                    cols:20



                    #Phase template
                    GridLayout:
                        width: 200
                        size_hint_x: None
                        cols:1
                        TextInput:
                            size_hint: 1, 0.06
                            halign: 'center'
                            hint_text:"Phase"
                        TextInput:
                            size_hint: 1, 0.06
                            halign: 'center'
                            hint_text:"Step"
                        Button:
                            size_hint:1, 0.07
                            text:"Add Step"
                        GridLayout:
                            cols:20

                            #Step template
                            GridLayout:
                                width: 100
                                size_hint_x: None
                                cols:1
                                TextInput:
                                    size_hint: 1, 0.06
                                    halign: 'center'
                                    hint_text:"Var1"
                                TextInput:
                                    size_hint: 1, 0.06
                                    halign: 'center'
                                    hint_text:"Var2"
                                Button:
                                    background_normal: ''
                                    background_color: 0.28,0.59,0.72,1
                                    text:"Test"
                                Button:
                                    size_hint:1, 0.07
                                    text:"Delete"


                        Button:
                            background_color: 0.8,0,0,1
                            size_hint:1, 0.07
                            text:"Delete"


You will see in the Kivy code that these is a commented secion called #Phase template. Basically on pressing the button Add Phase, this entire section and its children elements should be appended in the immediate parent GridLayout.

Here you can press the Add Phase button: Here you can press the Add Phase button:

Which will result in this:

Which will result in this

And then finally, pressing the Delete button should remove that specific appended section of code.

Again, no idea how to approach this from the Kivy language, which seems a bit rigid to work with. But I am sure what I want to do can be accomplished.


Solution

  • One way to accomplish that is to create a Phase class, and add a kv rule for building instances of Phase. Then, in kv, you can use Factory.Phase() to create new instances.

    Modify your kv as:

    #:import Factory kivy.factory.Factory
    <Interface>:
        GridLayout:
            padding:0
            size: root.width, root.height
            cols:1
            #Top buttons
            GridLayout:
                size_hint: 1, 0.07
                padding:0
                cols:1
                Button:
                    text:"Add Phase"
                    on_release: grid.add_widget(Factory.Phase())  # this adds another Phase
            #Chart areas
            ScrollView:
                do_scroll_y:False
                BoxLayout:
                    orientation: "vertical"
                    size_hint_x: None
                    width: self.minimum_width
                    GridLayout:
                        id: grid  # added id to identify where to add new Phase instances
                        size_hint_x: None
                        width: self.minimum_width
                        cols:20
    
                        # initial Phase instance
                        Phase:
    
    #Phase template
    <Phase@GridLayout>:
        width: 200
        size_hint_x: None
        cols:1
        TextInput:
            size_hint: 1, 0.06
            halign: 'center'
            hint_text:"Phase"
        TextInput:
            size_hint: 1, 0.06
            halign: 'center'
            hint_text:"Step"
        Button:
            size_hint:1, 0.07
            text:"Add Step"
        GridLayout:
            cols:20
    
            #Step template
            GridLayout:
                width: 100
                size_hint_x: None
                cols:1
                TextInput:
                    size_hint: 1, 0.06
                    halign: 'center'
                    hint_text:"Var1"
                TextInput:
                    size_hint: 1, 0.06
                    halign: 'center'
                    hint_text:"Var2"
                Button:
                    background_normal: ''
                    background_color: 0.28,0.59,0.72,1
                    text:"Test"
                Button:
                    size_hint:1, 0.07
                    text:"Delete"
    
    
        Button:
            background_color: 0.8,0,0,1
            size_hint:1, 0.07
            text:"Delete"
            on_release: root.parent.remove_widget(root)  # delete this Phase
    

    Key points are the <Phase@GridLayout> rule, the new grid id, and the use of Factory in the Add Phase Button.