使用Kivy库实现多下拉列表时部分下拉菜单失效问题求助
问题:Kivy多个下拉菜单仅部分正常工作
需要实现多个下拉列表,但部分下拉菜单无法正常工作。期望构造函数中的4个列表分别对应各自的主按钮正常显示并可使用,但实际仅有1个或偶尔2个下拉菜单能正常工作。
相关代码如下:
from kivy.uix.boxlayout import BoxLayout from kivy.uix.dropdown import DropDown from kivy.uix.button import Button from kivy.app import App class MidTopBar(): def __init__(self): self.dropDownLabels = { "File" : ['Example1', 'Example2','Example3', 'Example4'], "Edit" : ['Example5', 'Example6','Example7', 'Example8'], "View" : ['Example9', 'Example10','Example11', 'Example12'], "Help" : ['Example13', 'Example14','Example15', 'Example16'], } self.generate() def _generateDropdown(self, name, list): dropdown = DropDown(auto_width = True, width = 300) for index in range(len(list)): btn = Button(text=list[index], halign = 'left', valign = 'center', padding_x= 20, size_hint_y=None, background_color = (1, 1, 1, 1)) btn.bind(on_release=lambda btn: dropdown.select(list[index])) btn.bind(size= btn.setter('text_size')) dropdown.add_widget(btn) mainbutton = Button(text = name) dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x)) mainbutton.bind(on_release=dropdown.open) return mainbutton def _dropDowns(self): dropDownList = [] for key in self.dropDownLabels: dropDownList.append(self._generateDropdown(name= key, list = self.dropDownLabels[key])) return dropDownList def generate(self): dropDownList = self._dropDowns() self.Box = BoxLayout() for dropDown in dropDownList: self.Box.add_widget(dropDown) self.emptyBox = BoxLayout() self.superBox = BoxLayout(orientation='vertical') self.superBox.add_widget(self.Box) self.superBox.add_widget(self.emptyBox) class Example(App): def build(self): midTopBar :object = MidTopBar().superBox return midTopBar # Run the App of TextInput Widget in kivy if __name__ == "__main__": Example().run()
问题原因
- Lambda闭包陷阱:循环中绑定的
lambda btn: dropdown.select(list[index])并未捕获当前循环的index值,而是引用了循环变量的最终值。所有下拉项点击后都会选中对应列表的最后一个元素,导致功能异常。 - Dropdown被垃圾回收:
_generateDropdown方法中创建的Dropdown对象没有持久化引用,方法执行完毕后可能被Python垃圾回收机制回收,导致部分下拉菜单无法打开。
解决方案
修改_generateDropdown方法,解决闭包问题并保存Dropdown引用:
def _generateDropdown(self, name, list): dropdown = DropDown(auto_width=True, width=300) for item in list: btn = Button(text=item, halign='left', valign='center', padding_x=20, size_hint_y=None, background_color=(1, 1, 1, 1)) # 通过默认参数捕获当前item,避免闭包陷阱 btn.bind(on_release=lambda btn, x=item: dropdown.select(x)) btn.bind(size=btn.setter('text_size')) dropdown.add_widget(btn) mainbutton = Button(text=name) # 将Dropdown绑定到主按钮属性,防止被垃圾回收 mainbutton.dropdown = dropdown dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x)) mainbutton.bind(on_release=dropdown.open) return mainbutton
修改说明
- 遍历列表时直接使用
item而非索引,通过lambda的默认参数x=item捕获当前元素,避免闭包引用循环变量的问题。 - 将
dropdown赋值给mainbutton.dropdown,给Dropdown对象添加持久化引用,防止被垃圾回收。
内容的提问来源于stack exchange,提问作者Reza Besharat
相关产品推荐
相关产品推荐

