Search code examples
pythonkivykivy-languagekivymd

Python/Kivy - How to add a drop down item when a button is pressed: AttributeError 'super' object has no attribute '__getattr__'


I developed a simple UI in Kivy/KivyMD - Python. When a button is pressed a dropdown item should be visualized https://kivymd.readthedocs.io/en/1.1.1/components/dropdownitem/. Whilst the majority of examples consider a dropdown item already designed in the UI, I need to add it in the Py code, specifically in the on-release event associated to the button.

Here is my code:

from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.menu import MDDropdownMenu
from kivymd.uix.dropdownitem.dropdownitem import MDDropDownItem



Builder.load_string(
    """    
<View>:
    MDGridLayout:
        rows: 3
        id: layout
        padding: 100, 50, 100, 50
        
        
        MDRaisedButton:
            id: button
            text: 'CREATE DDI'
            on_release: root.Button_On_Click()
        

""")



class View(MDScreen):
    def __init__(self, **kwargs):
        super(View, self).__init__(**kwargs)
        
        
    def Button_On_Click(self):        
        myDdi = MDDropDownItem(
            # size_hint_x = None,
            # width = dp(100),
            # pos_hint = {"right": 1, "center_y": 0.5},
            text = 'SELECT POSITION')
        
        myMenu, scratch = Create_DropDown_Widget(myDdi, ['POS 1', 'POS 2', 'POS 3'], width=4)
        myDdi.on_release = myMenu.open()
        
        self.ids.layout.add_widget(myDdi)
        
       
    def Create_DropDown_Widget(self, drop_down_item, item_list, width):
        items_collection = [
            {
                "viewclass": "OneLineListItem",
                "text": item_list[i],
                "height": dp(56),
                "on_release": lambda x = item_list[i]: self.Set_DropDown_Item(drop_down_item, menu, x),
            } for i in range(len(item_list))
        ]
        
        menu = MDDropdownMenu(caller=drop_down_item, items=items_collection, width_mult=width)
        menu.bind()
        
        return menu, items_collection

         
    def Set_DropDown_Item(self, drop_down_item, menu, text_item):
        drop_down_item.set_item(text_item)
        menu.dismiss()
        
        
        

class MainApp(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.View = View()


    def build(self):
        self.title = ' DROP DOWN ITEM ADDED DYNAMICALLY'
        return self.View


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

Trying to execute the code, when I press the button I get this error:

line 49, in Button_On_Click
     text = 'SELECT POSITION')

File "C:\Users\\Miniconda3\lib\site-packages\kivymd\uix\behaviors\declarative_behavior.py", line 311, in __init__
     super().__init__(**kwargs)

File "C:\Users\\Miniconda3\lib\site-packages\kivymd\theming.py", line 1668, in __init__
     super().__init__(**kwargs)

File "C:\Users\\Miniconda3\lib\site-packages\kivy\uix\behaviors\button.py", line 121, in __init__
     super(ButtonBehavior, self).__init__(**kwargs)

File "C:\Users\\Miniconda3\lib\site-packages\kivy\uix\boxlayout.py", line 145, in __init__
     super(BoxLayout, self).__init__(**kwargs)

File "C:\Users\\Miniconda3\lib\site-packages\kivy\uix\layout.py", line 76, in __init__
     super(Layout, self).__init__(**kwargs)

File "C:\Users\\Miniconda3\lib\site-packages\kivy\uix\widget.py", line 357, in __init__
     super(Widget, self).__init__(**kwargs)

File "kivy\_event.pyx", line 262, in kivy._event.EventDispatcher.__init__
File "kivy\properties.pyx", line 520, in kivy.properties.Property.__set__
File "kivy\properties.pyx", line 567, in kivy.properties.Property.set
File "kivy\properties.pyx", line 606, in kivy.properties.Property._dispatch
File "kivy\_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1213, in kivy._event.EventObservers._dispatch
File "C:\Users\\Miniconda3\lib\site-packages\kivymd\uix\dropdownitem\dropdownitem.py", line 96, in on_text
     self.ids.label_item.text = text_item

File "kivy\properties.pyx", line 964, in kivy.properties.ObservableDict.__getattr__
 **AttributeError: 'super' object has no attribute '__getattr__'**

Solution

  • The problem is that KivyMD is not designed for use in declarative style, it is intended to be used in kv language. From the documentation:

    The KivyMD library does not support creating Kivy widgets in Python code in a declarative style.

    The documentation goes on to say:

    But you can still use the declarative style of creating Kivy widgets in Python code. To do this, you need to create a new class that will be inherited from the Kivy widget and the DeclarativeBehavior class

    However, this will not work with the MDDropDownItem. One way around this issue is to create the MDDropDowmItem instance without providing any args to the constructor. Here is a modified version of your Button_On_Click() method that uses this approach:

    def Button_On_Click(self):
        # myDdi = MDDropDownItem(
        #     # size_hint_x = None,
        #     # width = dp(100),
        #     # pos_hint = {"right": 1, "center_y": 0.5},
        #     text='SELECT POSITION')
        myDdi = MDDropDownItem()
        myDdi.text = 'SELECT POSITION'
    

    Another issue is your statement:

    myDdi.on_release = myMenu.open()
    

    That statement is executing myMenu.open() and assigning its return value to myDdi.on_release. If you are trying to assign a method to myDdi.on_release, you must assign the method, not its return value. Just replace that line with:

    myDdi.on_release = myMenu.open
    

    Note the lack of ().