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

Kivy自定义组件中Spinner调用add_key报错,Checkbox却正常

Kivy中Spinner触发add_key报错AttributeError: 'NoneType' object has no attribute 'add_key'的原因与解决方法

问题原因

  • 你在MainScreen的__init__方法中直接设置Spinner的text属性,这会立刻触发Spinner的on_text事件。
  • 此时MainScreen刚被实例化,还未被赋值给app.root(app.root要在App.build()返回后才会被系统设置),所以on_text绑定代码里的app.root是None,调用add_key自然抛出属性错误。
  • 而Checkbox的on_active事件是用户手动点击时才触发,此时App已完成初始化,app.root已经指向MainScreen实例,因此能正常调用add_key。

解决方案

方法1:延迟初始化Spinner属性

借助Clock.schedule_once延迟执行Spinner的属性设置,确保app.root已存在:

from kivy.app import App 
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivy.clock import Clock

selection = {}

pizza = ["margarita", "vegetarian", "american", "3 cheeses"]

KV="""
<Selection@BoxLayout>
    orientation: "horizontal"
    label_txt: ""
    key_name: ""
    Label:
        text: root.label_txt
    Spinner:
        id: spin_id
        text: ""
        on_text: app.root.add_key(root.key_name, self.text)

<Radio_op@BoxLayout>
    orientation: "horizontal"
    op: ""
    label_op: ""
    Label:
        text: root.label_op
    CheckBox:
        group:"topping"
        on_active: app.root.add_key("topping", root.op)

<MainScreen>
    BoxLayout:
        orientation: "vertical"
        Selection:
            id: pizza_sel
            label_txt: "pizza"
            key_name: "pizza"
        Label: 
            text: "Choose topping:"
        BoxLayout:
            orientation: "horizontal"
            Radio_op:
                label_op:"cream"
                op: "cream"
            Radio_op:
                label_op:"cheese"
                op: "cheese"
            Radio_op:
                label_op:"tomatoes"
                op: "tomatoes"
        BoxLayout:
            Label:
                id: pizza_lbl
            Label:
                id: topping_lbl
"""

class MainScreen(Screen):
    def __init__(self, **kwargs):
        super(MainScreen, self).__init__(**kwargs)
        # 延迟执行Spinner初始化
        Clock.schedule_once(self.init_spinner)

    def init_spinner(self, dt):
        self.ids.pizza_sel.ids.spin_id.text = pizza[0]
        self.ids.pizza_sel.ids.spin_id.values = pizza

    def add_key(self, name, text):
        selection[name] = text
        # 判空避免键不存在报错
        if "pizza" in selection:
            self.ids.pizza_lbl.text = selection["pizza"]
        if "topping" in selection:
            self.ids.topping_lbl.text = selection["topping"]

Builder.load_string(KV)

class MyApp(App):
    def build(self):
        return MainScreen()

if __name__ == '__main__':
    MyApp().run()

方法2:在事件绑定中增加判空逻辑

修改KV中Spinner的on_text绑定,先检查app.root是否有效再调用方法:

Spinner:
    id: spin_id
    text: ""
    on_text: if app.root: app.root.add_key(root.key_name, self.text)

同时建议在add_key方法中对selection的键做判空处理,避免因键不存在导致额外报错。

方法3:直接在KV中完成Spinner初始化

将Spinner的初始值和选项直接通过KV传递,避免在Python代码中触发on_text事件:
修改KV部分:

<Selection@BoxLayout>
    orientation: "horizontal"
    label_txt: ""
    key_name: ""
    pizza_options: []
    Label:
        text: root.label_txt
    Spinner:
        id: spin_id
        text: root.pizza_options[0] if root.pizza_options else ""
        values: root.pizza_options
        on_text: app.root.add_key(root.key_name, self.text)

<MainScreen>
    BoxLayout:
        orientation: "vertical"
        Selection:
            id: pizza_sel
            label_txt: "pizza"
            key_name: "pizza"
            pizza_options: pizza
        # 其余部分不变

同时删除MainScreen的__init__中设置Spinner属性的代码,这样初始化阶段不会触发on_text事件。

内容的提问来源于stack exchange,提问作者Flob74

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:30:51