Выборочное удаление дочерних виджетов из динамически заполняемого раскрывающегося списка в Kivy - PullRequest
0 голосов
/ 21 июня 2020

Я создал динамический c раскрывающийся список, который создает столько кнопок, сколько я указываю в поле ввода текста. Кнопки заполняются динамически, как и ожидалось, но я хотел бы выборочно удалить определенные кнопки (дочерние виджеты) из раскрывающегося списка и не очищать все дочерние виджеты одновременно. В файле kv по умолчанию определены две кнопки. Когда я использую функцию clear_widgets(), она удаляет все дочерние виджеты. Ниже приведен код.

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

Если я закомментирую В следующей строке раскрывающийся список продолжает добавлять кнопки кумулятивно при каждом щелчке, что понятно.

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

Я бы хотел добиться следующего:

  1. Файл kv должен загрузить 2 кнопки по умолчанию, как определено в файле kv
  2. Я указываю число в поле ввода текста. Например: 4
  3. В раскрывающемся списке должно быть 2 + 4 = 6 кнопок всего
  4. Если я определю новую кнопку для удаления 4 новых кнопок, должны быть удалены только последние 4 дочерних виджета . Исходные 2 кнопки в раскрывающемся списке должны остаться нетронутыми. удалить кнопку. Но я не уверен, как закодировать его внутри функции Python.

    Может кто-нибудь поможет?

    Спасибо

1 Ответ

1 голос
/ 22 июня 2020

Вы пробовали использовать remove_widget вместо clear_widgets? Например:

dropdown = App.get_running_app().root.get_screen('dropdown_screen').ids.dropdown
children_to_remove = []
for child in dropdown.children:
    if child.some_indicator == True:
        children_to_remove.append(child)

for child in children_to_remove:
    dropdown.remove(child)

(Вы не можете выполнить вызов dropdown.remove(child) в первом для l oop, потому что он изменит массив dropdown.children во время итерации)

...