首页 > 解决方案 > 从 Kivy 中动态填充的下拉列表中选择性地删除子小部件

问题描述

我创建了一个动态下拉列表,它创建了我在文本输入字段中指定的按钮。按钮正在按预期动态填充,但我想有选择地从下拉列表中删除某些按钮(子小部件),而不是同时清除所有子小部件。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()

基维

<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 函数中对其进行编码。

有人可以帮忙吗?

谢谢

标签: pythondrop-down-menukivykivy-language

解决方案


您是否尝试过使用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)在第一个 for 循环中进行调用,因为它会在迭代时改变 dropdown.children 数组)


推荐阅读