Kivy动态创建按钮on_press点击时仅删除最后一个按钮的问题
问题根源
这是Python闭包的晚绑定特性导致的:循环中定义的lambda函数里引用的循环变量i,不会在绑定事件的瞬间固定为当前循环的值,而是会在事件实际触发(点击按钮)时才读取i的最新值。当for循环执行完成后,i会停在最后一次循环的索引值(5个按钮场景下为4),因此所有按钮的点击回调最终都会执行self.remove_widget(Button_List[4]),永远只删除最后一个按钮。
修复方案
两种实现方式,推荐第二种,代码更简洁也不需要额外维护全局列表:
- 方案1:通过lambda默认参数固定当前索引
Python函数的默认参数会在函数定义时(即每次循环绑定事件的瞬间)完成求值,可以利用这个特性把每次循环的i值固定住,修改绑定代码为:Button_List[i].bind(on_press=lambda x, current_idx=i: self.remove_widget(Button_List[current_idx])) - 方案2:直接使用事件回调传入的按钮实例(推荐)
Kivy的on_press事件触发时,会自动将被点击的按钮对象作为参数传入回调函数(即lambda里接收的x参数),不需要通过索引从列表中查找按钮,直接删除传入的实例即可,无其他用途的话连全局Button_List都可以删除,绑定代码修改为:Button_List[i].bind(on_press=lambda x: self.remove_widget(x))
修正后完整代码
import kivy.uix.button as kb from kivy.app import App from kivy.uix.widget import Widget sentences = ['example_sentence1', 'example_sentence2', 'example_sentence3', 'example_sentence4', 'example_sentence5'] class Button_Widget(Widget): def __init__(self, **kwargs): super(Button_Widget, self).__init__(**kwargs) for i in range(len(sentences)): btn = kb.Button( text=sentences[i], pos=(self.width * i, self.height * i), size=(50, 50) ) btn.bind(on_press=lambda instance: self.remove_widget(instance)) self.add_widget(btn) class ButtonApp(App): def build(self): return Button_Widget() if __name__ == "__main__": ButtonApp().run()
注:原代码中未使用的GridLayout、lektionstextlesen导入以及全局Button_List变量已移除,代码逻辑更简洁。
内容的提问来源于stack exchange,提问作者Delicious_pie
相关产品推荐
相关产品推荐

