我非常清楚,已经多次询问过这个问题了。但在尝试以下解决方案后:
我得出结论,我需要帮助解决我的具体问题。列出的解决方案在我的具体案例中似乎不起作用。
以下情况:
我目前正在尝试使用kivy开发智能手机应用程序。因为我喜欢我的代码非常干净和清晰的结构我已经将我的Kivy代码分成几个kv文件。 python代码应该主要具有逻辑,仅此而已。为了使其正常工作,我需要引用不同kv文件中不同对象的实例。为了使我的问题清楚,我构建了一个相当简单的例子:
文件:attempt.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()
文件:attempt.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:
文件:attemptsupp.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()
picture of the Running Program - press the "Press"- button to get the error
使用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__'
即使我缩短了错误消息,也应该清楚会发生什么。我在要求C类中引用的ERRORBUTTON ID无法在字典中找到。现在问我的问题:
我怎样才能使它工作?我失踪了什么?
总之,我尝试过一些事情:
在我的案例中,这些尝试似乎都没有奏效。
总而言之:
我怎样才能让我的kivy References跨越不同的类正常工作,为什么ERRORBUTTON id不在我正在研究的词典中呢?
问题是由一个常见的错误引起的,id是相对于一个小部件,例如在你的情况下让我们分析表达式:
self.ids.ERRORBUTTON
谁是自我? self是RequirementC的实例。
你有什么实例?然后让我们看看实现RequirementC的.kv:
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.triggerC()
如果您发现唯一有权访问的ID是REQC,那么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
# ...
attempt.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"
attemptsuppC.kv
#:kivy 1.0
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.dispatch('on_release')
经过一番研究后,我发现了我正在寻找的答案。对于那些可能偶然发现我的问题的人来说:
class RequirementC(Widget):
def triggerC(self):
presentation.children[1].ids.ERRORBUTTON.text = "Requirement C met"
pass
通过回顾演示文稿的子项,可以使用“ids”方法。
但是对于那些想要使用它的人,我建议迭代孩子们找到正确的id而不是给出“硬”引用(children [1])。