Selectively delete child widgets (items) from a dynamically populated drop down

109 views
Skip to first unread message

Shoumik Das

unread,
Jun 21, 2020, 3:02:58 AM6/21/20
to Kivy users support
I have created a dynamic drop down list which creates as many buttons as I specify in a text input field. The buttons are getting dynamically populated as expected but I would like to selectively remove certain buttons (child widgets) from the dropdown and not clear all the child widgets at the same time. The kv file has two buttons defined by default. When I use the clear_widgets() function, it removes all the child widgets. Following is the code.

Python

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.properties import NumericProperty, StringProperty
from kivy.uix.dropdown import DropDown

class CustomTextInput(TextInput):
   
# Numeric property defined for use in both Python and Kivy (binding)
    max_characters
= NumericProperty(0)
   
# Override the default behaviour of the insert_text method
   
def insert_text(self, substring, from_undo=False):
       
if len(self.text)==self.max_characters and self.max_characters>0:
            substring
=""
       
TextInput.insert_text(self, substring, from_undo)


class ImageLabel(BoxLayout):
    source
= StringProperty('atlas://data/images/defaulttheme/audio-volume-high')
    text
= StringProperty('default text')


class ImageLabelButton(ButtonBehavior, ImageLabel):
   
pass


class ImageLabelButtonTop(ButtonBehavior, ImageLabel):
   
pass

class DropDownScreen(Screen):
   
def add_dd_values(self):
        dd_input
= App.get_running_app().root.get_screen('dropdown_screen').ids.textinput_num.text
       
print("TextInputBox: ", dd_input, "\n")
       
print("Length: ",len(dd_input))
       
print("Data Type: ", type(dd_input))
       
# Check if the text input box is empty and assign a default string of '0' so that int conversion and numerical operations do not fail with an error.
       
if dd_input == '':
            dd_input
="0"
       
print(int(dd_input)+1)
       
# Reset dropdown list. Clear all existing child widgets of dropdown including the original 2 buttons defined inside the kv file.
       
App.get_running_app().root.get_screen('dropdown_screen').ids.dropdown.clear_widgets()
       
for x in range(int(dd_input)):
           
if x%2==0:
               
print(x,"is an even number")
               
App.get_running_app().root.get_screen('dropdown_screen').ids.dropdown.add_widget(ImageLabelButton(source="twitter-48.png",text="Twitter"))
           
else:
               
print(x,"is an odd number")
               
App.get_running_app().root.get_screen('dropdown_screen').ids.dropdown.add_widget(ImageLabelButton(source="linkedin-2-48.png",text="LinkedIn"))


class MyScreenManager(ScreenManager):
   
pass


class DropApp(App):
   
def build(self):
       
# Initialize root widget
       
# Screen Manager instance
        sm
= MyScreenManager()
       
# DropDown Screen Instance
        dds
= DropDownScreen()
        sm
.add_widget(dds)
       
return sm


if __name__ == '__main__':
   
# Run application
   
DropApp().run()

Kivy

<DropDownScreen>:
    name: 'dropdown_screen'
    canvas.before:
        Color:
            rgba: 255/255, 255/255, 255/255, 1
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            orientation: 'horizontal'
            Label:
                text: 'No. of drop-down buttons:'
                markup: True
                color: 0, 0, 0, 1
            CustomTextInput:
                id: textinput_num
                max_characters: 2
                multiline: False
                input_filter: 'int'
        BoxLayout:
            ImageLabelButtonTop: # conflict in the on_release defined for ImageLabelButton and this instance.
                id: parent_button
                source: 'expand-arrow-48.png'
                text: 'Expand Drop-Down'
                on_release:
                    root.add_dd_values()
                    dropdown.open(self)
                    print("self in ImageLabelButtonTop points to ", self, "\n")
                on_parent: dropdown.dismiss()
                size_hint_y: None
                height: '48dp'
            DropDown:
                id: dropdown
                on_select:
                    # parent_button.text = str(args[1]) # args is a reserved keyword which returns only two values: object alias (0) and data (1).
                    parent_button.text = args[1][0]
                    parent_button.source = args[1][1]
                    print("Invoked inside dropdown")
                    print(args, app.root, root, self, "\n") # root - Screen, self - DropDown object
                ImageLabelButton:
                    source: 'twitter-48.png'
                    text: 'Twitter'
                ImageLabelButton:
                    source: 'linkedin-2-48.png'
                    text: 'LinkedIn'
        BoxLayout:
            Label:
                text: 'Test dynamic drop-down'
                markup: True
                color: 0, 0, 0, 1


<ImageLabel>:
    orientation: 'horizontal'
    size_hint_y: None
    height: '48dp'
    spacing: 1
    Image:
        keep_ratio: True
        source: root.source  # root - ImageLabel
    Label:
        markup: True
        text: root.text  # root - ImageLabel
        color: 0, 0, 0, 1


<ImageLabelButton>:
    on_release:
        # Tuple returned in dropdown.select() method to pass two values bundled as one.
        app.root.get_screen('dropdown_screen').ids.dropdown.select((self.text, self.source))
        print("Invoked inside ImageLabelButton rule")
        print(app.root, root, self, "\n") # app.root - Screen, root - ImageLabelButton object, self - ImageLabelButton object

<CustomTextInput>:
    use_bubble: True
    use_handles: True

If I comment out the following line, the drop down list keeps adding buttons cumulatively on every click, which is understandable.

App.get_running_app().root.get_screen('dropdown_screen').ids.dropdown.clear_widgets()

What I would like to achieve is this:

  1. The kv file should load 2 buttons by default as defined in the kv file
  2. I specify a number in the text input field. E.g: 4
  3. The drop down should have 2 + 4 = 6 buttons in total
  4. If I define a new button to remove the 4 new buttons, only the last 4 child widgets should be deleted. The original 2 buttons should remain intact in the dropdown list.

I guess, it may work if I store the reference of the dynamically created child widgets in a list or dictionary and then delete them through a separate remove button. But I am not sure how to code it inside a Python function.


Can someone please help?


Thanks

Elliot Garbus

unread,
Jun 21, 2020, 8:52:09 AM6/21/20
to kivy-...@googlegroups.com

I created the method remove_other_2widets.  I assumed that the text on the dropdowns would be unique, and delete the widgets that were not Twitter on LinkedIn.  I changed the text on the dynamically created buttons to make my assumption true.

 

 

class DropDownScreen(Screen):
   
def add_dd_values(self):
        dd_input = App.get_running_app().root.get_screen(
'dropdown_screen'
).ids.textinput_num.text
       
# note: self == App.get_running_app().root.get_screen('dropdown_screen')
       
print("TextInputBox: ", dd_input, "\n")
       
print("Length: ",len(dd_input))
       
print("Data Type: ", type(dd_input))
       
# Check if the text input box is empty and assign a default string of '0' so that int conversion and numerical operations do not fail with an error.
       
if dd_input == '':
            dd_input=
"0"
       
print(int(dd_input)+1)
       
# Reset dropdown list. Clear all existing child widgets of dropdown including the original 2 buttons defined inside the kv file.
       
self.remove_other_widgets()
       
#App.get_running_app().root.get_screen('dropdown_screen').ids.dropdown.clear_widgets()
       
for x in range(int(dd_input)):
           
if x%2==0:
               
print(x,"is an even number")
                App.get_running_app().root.get_screen(
'dropdown_screen').ids.dropdown.add_widget(ImageLabelButton(source="twitter-48.png",text="Not Twitter"))

           
else:
               
print(x,"is an odd number"
)
                App.get_running_app().root.get_screen(
'dropdown_screen').ids.dropdown.add_widget(ImageLabelButton(source="linkedin-2-48.png",text="Not LinkedIn"))

def remove_other_widgets(self):
    children =
self.ids.dropdown.container.children.copy()
   
for w in children:
       
if w.text not in ['Twitter', 'LinkedIn']:
           
self.ids.dropdown.container.remove_widget(w)

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/8723cb5c-e7f5-41c4-88f4-22db6087b04eo%40googlegroups.com.

 

Shoumik Das

unread,
Jun 21, 2020, 1:30:08 PM6/21/20
to Kivy users support
Hi Eliott,

That solved my requirement. Thanks for your help. One small question: What is the purpose of  "container" in the line:

children = self.ids.dropdown.container.children.copy()

Thanks

Elliot Garbus

unread,
Jun 21, 2020, 1:56:41 PM6/21/20
to kivy-...@googlegroups.com

The container holds the dropdown items:

https://kivy.org/doc/stable/api-kivy.uix.dropdown.html?highlight=dropdown#kivy.uix.dropdown.DropDown.container

 

This concept shows up in a few of the widgets.  I first ran into it with the TabbedPanel.

 

 

From: Shoumik Das
Sent: Sunday, June 21, 2020 10:30 AM
To: Kivy users support

--

You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.

Shoumik Das

unread,
Jun 24, 2020, 11:48:37 AM6/24/20
to kivy-...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages