Kivy DropDown implementation has error “Cannot add to window, it already has a parent ” when calling .open(self)

My goal is a drop down list similar to those seen in Windows File Explorer:

enter image description here

I’m experimenting with DropDown with the following code:

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()

my.kv

<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'

I get the following error on the line 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:

To implement the suggestion, I sub-classed DropDown and Label and overrode the on_select and on_touch_down methods, respectively. I also had to override the __init__ method of DropDown in my subclass, so that I could get access to the TextInput box and update its content on with on_select. This seems awkward and adds a lot of code, am I missing something that would cut down on the amount and complexity of this sort of code?

updated 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()

Updated my.kv

<MainApp>:
    DropDownTextInput:

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

Thanks again!

Answer

DropDowns are designed to be opened by calling their open method, which adds them to the widget tree in such a way that they appear on top of everything and with the correct position, but you have added it to the widget tree yourself by putting it in your kv rule. That is why it has a parent already.

You should not declare it as a child of CustomDropDown, instead create a new instance and call the open method of that.