KivyMD动态添加MDDropDownItem触发AttributeError问题求助
动态添加MDDropDownItem触发AttributeError的解决方法
问题场景
使用Python/KivyMD开发UI,需要点击按钮时动态添加MDDropDownItem,而非预先在KV中定义。点击按钮后触发如下错误:
AttributeError: 'super' object has no attribute '__getattr__'
原始代码
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 = self.Create_DropDown_Widget(myDdi, ['POS 1', 'POS 2', 'POS 3'], width=4) myDdi.on_release = lambda: 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": self.dp(56), "on_release": lambda x = item_list[i], menu=self.Create_DropDown_Widget.__locals__['menu']: 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) 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()
错误详情
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__'**
问题原因
- 组件初始化顺序问题:动态实例化MDDropDownItem时直接设置text属性,会触发
on_text方法,但此时MDDropDownItem的内部UI组件(如label_item)还未通过KV解析完成初始化,导致访问self.ids.label_item抛出错误。 - 变量捕获错误:
Create_DropDown_Widget方法中,列表推导式引用的menu在推导式执行时还未定义,后续点击菜单选项会出现变量引用异常。
修复方案
- 延迟设置text属性:先实例化MDDropDownItem,添加到布局后再设置text,确保内部组件已初始化完成。
- 修正菜单选项生成逻辑:先创建
MDDropdownMenu实例,再基于该实例生成菜单选项,确保lambda能正确捕获menu对象。
修复后的代码
from kivy.lang import Builder from kivy.metrics import dp 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 Button_On_Click(self): # 先实例化,不设置text myDdi = MDDropDownItem() # 添加到布局后再设置text,确保内部组件已初始化 self.ids.layout.add_widget(myDdi) myDdi.text = 'SELECT POSITION' myMenu = self.Create_DropDown_Widget(myDdi, ['POS 1', 'POS 2', 'POS 3'], width=4) myDdi.on_release = lambda: myMenu.open() def Create_DropDown_Widget(self, drop_down_item, item_list, width): # 先创建menu实例,再生成选项 menu = MDDropdownMenu(caller=drop_down_item, items=[], width_mult=width) items_collection = [ { "viewclass": "OneLineListItem", "text": item, "height": dp(56), "on_release": lambda x=item, m=menu: self.Set_DropDown_Item(drop_down_item, m, x), } for item in item_list ] menu.items = items_collection return menu def Set_DropDown_Item(self, drop_down_item, menu, text_item): drop_down_item.set_item(text_item) menu.dismiss() class MainApp(MDApp): def build(self): self.title = 'DROP DOWN ITEM ADDED DYNAMICALLY' return View() if __name__ == '__main__': MainApp().run()
内容的提问来源于stack exchange,提问作者eljamba
相关产品推荐
相关产品推荐

