Реализация раскрывающегося списка Kivy содержит ошибку «Не удается добавить в окно, у него уже есть родительский » при вызове .открыть(самостоятельно)

#python #kivy #kivy-language

Вопрос:

Моя цель-выпадающий список, аналогичный тем, которые можно увидеть в Проводнике Windows:

введите описание изображения здесь

Я экспериментирую DropDown со следующим кодом:

main.py

 import kivy
kivy.require('2.0.0')

from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.textinput import TextInput
from kivy.properties import ObjectProperty


class CustomDropDown(TextInput):

    droplist = ObjectProperty(None)
    
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            self.droplist.open(self)
        return super().on_touch_down(touch)


class MainApp(AnchorLayout):
    pass

class MyApp(App):

    def build(self):
        return MainApp()


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

мой.кв

 <MainApp>:
    CustomDropDown:

<CustomDropDown>
    text: 'Select an option..'
    size_hint_y: None
    height: 30

    droplist: options

    DropDown:
        id: options
        Label:
            text: 'Option 1'
        Label:
            text: 'Option 2'
        Label:
            text: 'Option 3'
 

Я получаю следующую ошибку в строке self.droplist.open(self) :

Cannot add <kivy.uix.dropdown.DropDown object at 0x00000223791F39E0> to window, it already has a parent <__main__.CustomDropDown object at 0x00000223791BA580>

I think this should be working, and after some time troubleshooting I still can’t figure out why it doesn’t. Does anyone spot my error?

—UPDATE 9/12/2021 3:30PM—

Thank you @inclement for the suggestion, thanks to your response I was able to move forward. However, I now have a follow-up question:

Чтобы реализовать это предложение, я DropDown подклассировал и Label и отменил методы on_select и on_touch_down , соответственно. Мне также пришлось переопределить __init__ метод DropDown в моем подклассе, чтобы я мог получить доступ к TextInput окну и обновить его содержимое с on_select помощью . Это кажется неудобным и добавляет много кода, не упускаю ли я чего-то, что уменьшило бы объем и сложность такого рода кода?

обновлено main.py

 import kivy
kivy.require('2.0.0')

from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput


class CustomDropDown(TextInput):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.dropdown = DropDownList(self)

        for item in ['Option 1', 'Option 2', 'Option 3']:
            lbl = DropDownItem(text=item, size_hint_y=None, height=30)
            self.dropdown.add_widget(lbl)
        
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            self.dropdown.open(self)
        return super().on_touch_down(touch)


class DropDownList(DropDown):

    def __init__(self, user_choice, **kwargs):
        super().__init__(**kwargs)
        self.user_choice = user_choice

    def on_select(self, data):
        setattr(self.user_choice, 'text', data)


class DropDownItem(Label):

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            dropdown = self.parent.parent
            dropdown.select(self.text)
        return super().on_touch_down(touch)
            

class MainApp(AnchorLayout):
    pass

class MyApp(App):

    def build(self):
        return MainApp()


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

Обновил свой.kv

 <MainApp>:
    DropDownTextInput:

<DropDownTextInput>
    text: 'Select an option..'
    size_hint_y: None
    height: 30
 

Еще раз спасибо!

Ответ №1:

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

Вы не должны объявлять его дочерним CustomDropDown , вместо этого создайте новый экземпляр и вызовите open его метод.

Комментарии:

1. Ага! Это помогло мне отвлечься, большое вам спасибо! Однако код стал немного сложнее, возможно, вы увидите мое обновление к сообщению с моей рабочей реализацией