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__'**
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 ()
.