Search code examples
pythonkivykivy-language

Add and remove Kivy Layout using CheckBox


I've setup a CheckBox in Kivy which, when ticked, adds a layout widget with some Labels and TextInputs but I want it to remove the layout when the box is unticked. Ticking the checkbox adds the layout with the right size and position (below the GridLayout with the CheckBox, within the input_layout BoxLayout) but when I untick it, the layout doesn't go away. I've checked if it will remove the input_layout layout, and it does, but not the one added through the python code.

The print statement prints False when the box is unticked so I know that it's working somewhat properly but the self.ids.input_layout.remove_widget(self.layout) isn't removing the layout.

I tried setting up a template GridLayout to which the Labels and TextInputs would then be added through the CheckBox and python code. This setup partially worked; the Checkbox would add the widgets, and unticking it then removed them, but I couldn't add them back again as the whole GridLayout was gone.

The other CheckBoxes will add/remove different layouts so my first setup attempt is more ideal, if it can actually work.

Update:

Updated code with runnable example of the problem.

Python

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.metrics import dp

Builder.load_file("process_data_example_design.kv")

class ProcessDataEx(BoxLayout):

    def add_layout(self, checkbox, value):
        self.layout = GridLayout(cols=2, rows=4, size_hint_y=None, height=self.height*0.275, spacing=dp(8), padding=dp(8))
        self.layout.add_widget(Label(text="Histogram particle label:", size_hint_x=None, width=self.width*0.33))
        self.layout.add_widget(TextInput())
        self.layout.add_widget(Label(text="Dumps to iterate:", size_hint_x=None, width=self.width*0.33))
        self.layout.add_widget(TextInput())
        self.layout.add_widget(Label(text="X-axis max:", size_hint_x=None, width=self.width*0.33))
        self.layout.add_widget(TextInput())
        self.layout.add_widget(Label(text="Y-axis max:", size_hint_x=None, width=self.width*0.33))
        self.layout.add_widget(TextInput())
        if value:
            self.ids.input_layout.add_widget(self.layout)
        else:
            self.ids.input_layout.remove_widget(self.layout)
            print(value)

class ProcessDataApp(App):
    def build(self):
        return ProcessDataEx()

if __name__ == '__main__':
    ProcessDataApp().run()

Kivy

#:kivy 2.0.0

<ProcessDataEx>:
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            id: input_layout
            orientation: 'vertical'
            GridLayout:
                cols: 3
                rows: 2
                height: self.minimum_height
                spacing: dp(8)
                padding: dp(8)
                Label:
                    text: 'Move data'
                    size_hint_y: None
                    height: self.texture_size[1]
                    halign: 'right'
                Label:
                    text: 'Histograms'
                    size_hint_y: None
                    height: self.texture_size[1]
                    halign: 'right'
                Label:
                    text: 'Log Histograms'
                    size_hint_y: None
                    height: self.texture_size[1]
                    halign: 'right'
                CheckBox:
                    size_hint_y: None
                    height: root.height * 0.05
                CheckBox:
                    size_hint_y: None
                    height: root.height * 0.05
                CheckBox:
                    size_hint_y: None
                    height: root.height * 0.05
                    on_active: root.add_layout(*args)

Solution

  • the mistake you make is creating a new instance of laoyut every time you call add_layout method so the remomve widget did not work because the new widget it does not exists in the input_layout the solution is to create the layout when is not exist so your python code should be like this

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.label import Label
    from kivy.uix.textinput import TextInput
    from kivy.metrics import dp
    
    Builder.load_file("process_data_example_design.kv")
    
    class ProcessDataEx(BoxLayout):
        # initialize the layout variable
        layout=None
        def add_layout(self, checkbox, value):
            
            if value:
                self.layout = GridLayout(cols=2, rows=4, size_hint_y=None, height=self.height * 0.275, spacing=dp(8),
                                         padding=dp(8))
                self.layout.add_widget(Label(text="Histogram particle label:", size_hint_x=None, width=self.width * 0.33))
                self.layout.add_widget(TextInput())
                self.layout.add_widget(Label(text="Dumps to iterate:", size_hint_x=None, width=self.width * 0.33))
                self.layout.add_widget(TextInput())
                self.layout.add_widget(Label(text="X-axis max:", size_hint_x=None, width=self.width * 0.33))
                self.layout.add_widget(TextInput())
                self.layout.add_widget(Label(text="Y-axis max:", size_hint_x=None, width=self.width * 0.33))
                self.layout.add_widget(TextInput())
                self.ids.input_layout.add_widget(self.layout)
            else:
                self.ids.input_layout.remove_widget(self.layout)
                print(value)
    
    class ProcessDataApp(App):
        def build(self):
            return ProcessDataEx()
    
    if __name__ == '__main__':
        ProcessDataApp().run()