python - AttributeError: 'super' object has no attribute '__getattr__' 在 Kivy 中使用带有多个 kv 文件的 BoxLayout 时出错
问题描述
我很清楚,这个问题已经被问过好几次了。但是在尝试了以下解决方案之后:
- Python - Kivy:AttributeError:'super'对象在尝试获取self.ids时没有属性'__getattr__'
- Python/Kivy AttributeError:“超级”对象没有属性“__getattr__”
- 如何在不获取 AttributeError 的情况下访问 kivy 中另一个类的对象属性:'super' object has no attribute '__getattr__'
- AttributeError:“超级”对象没有属性“__getattr__”
我已经得出结论,我需要帮助解决我的具体问题。列出的解决方案似乎不适用于我的具体情况。
以下情况:
我目前正在尝试使用 kivy 为智能手机开发应用程序。因为我喜欢我的代码非常干净和清晰的结构,所以我将我的 Kivy 代码拆分为几个 kv 文件。python 代码应该主要具有逻辑,仅此而已。为了让它正常工作,我需要在不同的 kv 文件中引用不同对象的实例。为了使我的问题清楚,我构建了一个相当简单的示例:
文件:尝试.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.factory import Factory
from kivy.uix.label import Label
from kivy.lang import Builder
x= 1
class ComplexBox(Widget):
def testit(self):
self.ids.layout.add_widget(Label(text = "Requirement A met."))
def addsome(self):
global x
self.ids.layout.add_widget(SomeWidget(id="XXX"+str(x)))
x = x +1
pass
class SomeWidget(Widget):
def change(self):
self.ids.REQB.text = "Requirement B met."
pass
class RequirementC(Widget):
def triggerC(self):
self.ids.ERRORBUTTON.text = "Requirement C met"
pass
class Attempt(App):
def build(self):
return presentation
pass
presentation = Builder.load_file("attempt.kv")
Attempt().run()
文件:try.kv
#:kivy 1.0
#:include attemptsupp.kv
#:include attemptsuppC.kv
# root
<ComplexBox>:
BoxLayout:
id: layout
size: root.size
Button:
id: ERRORBUTTON
text: "add"
on_press: root.addsome()
on_release: root.testit()
BoxLayout:
orientation: 'vertical'
ComplexBox:
RequirementC:
文件:trysupp.kv
#:kivy 1.0
# rules for the widget
<SomeWidget>:
BoxLayout:
pos: root.pos
size: root.size
orientation: "vertical"
Label:
id: REQB
text: "hello"
Button:
text: "world"
on_release: root.change()
文件:attemptsuppC.kv
#:kivy 1.0
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.triggerC()
使用 kivy 版本 1.10 和 Python 版本 3.7.2 运行,该程序首先可以正常启动。但是,当我按下标有“按下”且 ID 为 ERRORBUTTON 的按钮时,我收到此错误:
...--default --nodebug --client --host localhost --port 57777...\attempt.py "
[INFO ] [Logger ] Record log in...\.kivy\logs\kivy_19-03-15_31.txt
[INFO ] [Kivy ] v1.10.1
[INFO ] [Python ] v3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018,
...
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[WARNING] [Lang ] attemptsupp.kv has already been included!
[WARNING] [Lang ] attemptsuppC.kv has already been included!
[INFO ] [Base ] Start application main loop
[INFO ] [GL ] NPOT texture support is available
[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "kivy\properties.pyx", line 838, in kivy.properties.ObservableDict.__getattr__
KeyError: 'ERRORBUTTON'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...\ptvsd_launcher.py", line 45, in <module>
main(ptvsdArgs)
...
File "e:\Daten\Github_Projects\pc-clicker\attempt.py", line 35, in <module>
Attempt().run()
File "...\lib\site-packages\kivy\app.py", line 826, in run
runTouchApp()
...
File ...\lib\site-packages\kivy\lang\builder.py", line 64, in custom_callback
exec(__kvlang__.co_value, idmap)
File ...\attemptsuppC.kv", line 7, in <module>
on_release: root.triggerC()
File "...\attempt.py", line 25, in triggerC
self.ids.ERRORBUTTON.text = "Requirement C met"
File "kivy\properties.pyx", line 841, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
即使我缩短了错误消息,也应该清楚发生了什么。在字典中找不到我在 RequirementC 类中引用的 ERRORBUTTON id。现在我的问题:
我怎样才能让它工作?我缺少什么?
简而言之,我尝试了几件事:
- 我尝试将 BoxLayouts 包装在 Screen 中并通过 screenmanager 访问它们。
- 我尝试在 python 代码中重新排列顺序。(例如先加载主kv文件)
- 我尝试使用 Builder Factory 并在那里注册不同的类。
- 我已经尝试更改参考。(例如 self.ids.['ERRORBUTTON']...)
在我的情况下,这些尝试似乎都没有奏效。
所以总结一下:
如何让不同类的 kivy 引用正常工作,为什么 ERRORBUTTON id 不在我正在查看的字典中?
解决方案
问题是由一个常见错误引起的,ids是相对于一个小部件的,例如在你的情况下让我们分析表达式:
self.ids.ERRORBUTTON
自我是谁?self 是 RequirementC 的实例。
你有什么实例?然后让我们看看实现 RequirementC 的 .kv:
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.triggerC()
如果您注意到唯一可以访问 REQC 的 id,那么 RequirementC 的 id ERRORBUTTON 不存在。
那么 id ERRORBUTTON 属于哪个类呢?因此,让我们回顾一下 ERRORBUTTON 的创建位置:
# ...
<ComplexBox>:
BoxLayout:
id: layout
size: root.size
Button:
id: ERRORBUTTON
text: "add"
on_press: root.addsome()
on_release: root.testit()
# ...
如您所见,ERRORBUTTON 是 ComplexBox 的 id。
有了上一部分提到的内容,我们已经知道了问题的原因。在给出解决方案之前,我们首先了解编程的基本原则:类是一种行为的抽象,它必须清楚地定义你想要暴露给外部(因此,如果你查看任何库的文档,请不要记录所有方法或所有类,因为这个想法是抽象类,也就是说,使用该库的人不想知道它在内部是如何以如此精确的方式工作的),因此最好设计思考类将具有哪些方法. 比如说我们创建了一个Person类,这个类有一定的大小或者重量等属性,如果你觉得有必要暴露你的心脏或者大脑的重量怎么办?嗯,不。你的情况也是如此。
解决方案是公开事件 on_release,使其成为 RequirementC 类的一部分,此外还公开 ERRORBUTTON 作为属性(在我的情况下,我不喜欢使用 id,因为它们使代码的可读性降低),然后使连接在一个具有共同范围的地方。
*.py
# ...
class RequirementC(Widget):
def __init__(self, **kwargs):
self.register_event_type('on_release')
super().__init__(**kwargs)
def on_release(self):
pass
# ...
尝试.kv
#:kivy 1.0
#:include attemptsupp.kv
#:include attemptsuppC.kv
# root
<ComplexBox>:
error_button: ERRORBUTTON # <---
BoxLayout:
id: layout
size: root.size
Button:
id: ERRORBUTTON
text: "add"
on_press: root.addsome()
on_release: root.testit()
BoxLayout:
orientation: 'vertical'
ComplexBox:
id: complex_box
RequirementC:
on_release: complex_box.error_button.text = "Requirement C met"
尝试suppC.kv
#:kivy 1.0
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.dispatch('on_release')
推荐阅读
- c# - bouncycastle 如何确定 ECDSA 签名的第一个字节(恢复 ID)
- python - tkinter:GUI启动时如何检测“Checkbutton”?
- terminal - 如何为透明的PNG文件添加背景?
- node.js - DocuSign API 连接密钥
- flutter - 如何在颤动中的每次重复动画之间添加延迟
- amazon-web-services - 是否可以在带有 S3 存储桶的 AWS 负载均衡器侦听器上使用路径基础路由?
- google-bigquery - Google BigQuery - 有没有一种方法可以在多个列中搜索值列表。像 CONTAINS_SUBSTR 或 REGEXP_CONTAINS
- sql - Logstash 实例因 ConnectionJdbcException 而失败
- r - 是否可以从 Power BI 获取用户输入并传输到 R 进行分析和计算?
- visual-studio-code - 如何使用键盘快捷键重置 vscode 终端窗格大小?