You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:36:23