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

Kivy应用开发求助:切换Widget时触发AttributeError错误

解决Kivy替换部件时的AttributeError问题

我正在用Kivy开发一款趣味手机应用,这是我的首个大型编程项目。现在遇到功能瓶颈:通过climbtype函数的输出值触发条件判断,想把"grade"部件替换为Sport_Spinner_widget,但每次尝试都会抛出以下错误:

File "kivy\properties.pyx", line 964, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'. Did you mean: '__setattr__'?

确定问题出在add_sport_grades方法的目标代码里,尝试过改用LogClimb类中可行的self.ids.container.add_widget(customewidget)路径,但没用。

相关代码

.py文件

from kivy import Config
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen

class ClimbWidget(BoxLayout):
  def remove(self):
    self.parent.remove_widget(self)

  def climbtype(self, value):
    if value == "Sport":
      self.add_sport_grades()

  def add_sport_grades(self):
    customewidget = ClimbWidget()
    sportspinner = Sport_Spinner_widget()
    self.ids.grade.add_widget(sportspinner)  # still not functioning?????!!!


class LogClimb(Screen):
  def add_climb_inputs(self):
    customewidget = ClimbWidget()
    self.ids.container.add_widget(customewidget)

  class WindowManager(ScreenManager):
    pass


kv = Builder.load_file("LogBook.kv")


class LogBook(App):
  def build(self):
    return kv


LogBook().run()

.kv文件

LogClimb:

<LogClimb>:
    name: "LogClimb"
    BoxLayout:
        size_hint_y: 0.1
        pos_hint: {"y" : 0.9}
        Label:
            text: "log Climb"
        Button:
            text: "Add Climb"
            on_press: root.add_climb_inputs()
    GridLayout:
        cols: 1
        size_hint_y: 0.9
        size_hint_x: 2
        id: container

<ClimbWidget>
    height: 40
    size_hint_y: None
    Label:
        text: "date"
    Spinner:
        id: climbtype
        text: "Climb type"
        values: ["Sport", "Boldering", "Trad"]
        on_text: root.climbtype(climbtype.text)
    TextInput:
        id: climbvenu
        text: "Venue"
        font_size: "20dp"
        multiline: True
        #on_text_validate: root.on_text_validate(self)
    Spinner:
        id: "grade"
        text: "grade"
        font_size: "20dp"
    Button:
        text: "-"
        on_press: root.remove()

<Sport_Spinner_widget>:
    height: 40
    size_hint_y: None
    Spinner:
        text: "hi"
        values: ["1", "2", "2+", "3a", "3b", "3c", "4a", "4b", "4c", "5a", "5b", "5c", "5c", "6a", "6a+"]

解决方案

错误根源及修复要点

  1. kv文件中id的引号错误
    Kivy中控件的id不需要加引号,你写的id: "grade"会导致self.ids.grade无法找到对应部件,正确写法是id: grade。

  2. 未定义自定义部件类
    Python代码中缺少Sport_Spinner_widget类的定义,Kivy无法识别这个自定义部件,需要补充类定义。

  3. 替换部件的逻辑错误
    Spinner是单个控件,不能添加子部件。正确逻辑是先移除原有的grade Spinner,再将自定义部件添加到ClimbWidget的布局中。

修改后的代码

.py文件

from kivy import Config
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen

# 补充自定义部件类定义
class Sport_Spinner_widget(BoxLayout):
    pass

class ClimbWidget(BoxLayout):
    def remove(self):
        self.parent.remove_widget(self)

    def climbtype(self, value):
        if value == "Sport":
            self.add_sport_grades()

    def add_sport_grades(self):
        # 移除原grade Spinner
        original_grade = self.ids.grade
        self.remove_widget(original_grade)
        # 创建并添加自定义部件
        sportspinner = Sport_Spinner_widget()
        self.add_widget(sportspinner)


class LogClimb(Screen):
    def add_climb_inputs(self):
        customewidget = ClimbWidget()
        self.ids.container.add_widget(customewidget)

# 修正WindowManager的缩进(原代码嵌套在LogClimb中,会导致问题)
class WindowManager(ScreenManager):
    pass


kv = Builder.load_file("LogBook.kv")


class LogBook(App):
    def build(self):
        return kv


LogBook().run()

.kv文件

LogClimb:

<LogClimb>:
    name: "LogClimb"
    BoxLayout:
        size_hint_y: 0.1
        pos_hint: {"y" : 0.9}
        Label:
            text: "log Climb"
        Button:
            text: "Add Climb"
            on_press: root.add_climb_inputs()
    GridLayout:
        cols: 1
        size_hint_y: 0.9
        size_hint_x: 2
        id: container

<ClimbWidget>
    height: 40
    size_hint_y: None
    orientation: 'horizontal'  # 明确布局方向,确保部件排列一致
    Label:
        text: "date"
    Spinner:
        id: climbtype
        text: "Climb type"
        values: ["Sport", "Boldering", "Trad"]
        on_text: root.climbtype(climbtype.text)
    TextInput:
        id: climbvenu
        text: "Venue"
        font_size: "20dp"
        multiline: True
        #on_text_validate: root.on_text_validate(self)
    Spinner:
        id: grade  # 去掉引号
        text: "grade"
        font_size: "20dp"
    Button:
        text: "-"
        on_press: root.remove()

<Sport_Spinner_widget>:
    height: 40
    size_hint_y: None
    Spinner:
        text: "hi"
        values: ["1", "2", "2+", "3a", "3b", "3c", "4a", "4b", "4c", "5a", "5b", "5c", "5c", "6a", "6a+"]

额外修正:原代码中WindowManager类嵌套在LogClimb类内部,这会导致ScreenManager无法正常工作,已调整为独立类。

内容的提问来源于stack exchange,提问作者Sam eldridge Darkfox

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 11:29:50