如何在不使用 `self.parent.parent 的情况下更改另一个 Kivy Widget 的 child 的属性。 ...`?

How to change properties of another Kivy Widget's child without using `self.parent.parent. ...`?

我的问题是标题。它可能与 相同,但我对那里的答案或下面的最小工作示例不满意。那是因为如果我要移动 child 我是从一个“嵌套层”向上或向下进行调用(例如,在我的示例中:将“特殊按钮”放在 parent 的另一个布局中BoxLayout) 回调函数将不再起作用。

如何正确地做到这一点(以及预期的 kivy 方式)?

最小示例:

from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import ObjectProperty

Builder.load_string('''
<RootWidget>:
  button1: b1
  button2: b2
  BoxLayout:
    orientation: 'horizontal'
    size: root.size
    Button:
      id: b1
      text: 'I am the normal one'
    SpecialButton:
      id: b2
      text: 'I am the special one'
<SpecialButton>:
  on_release: root.clickityclick()
''')

class RootWidget(Widget):
  button1 = ObjectProperty(None)
  button2 = ObjectProperty(None)

class SpecialButton(Button):
  def clickityclick(self):
    self.parent.parent.button1.disabled = True  ### <----- How to do this properly?

class MinimalExampleApp(App):
  def build(self):
    return RootWidget()

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

@inclement 的评论包含我问题的答案(具体来说:来自 his article(“选项 1”)的倒数第二个示例)

选项 1:在要从中引发更改的小部件中引入 kivy-property。在 kv 规则中,将接收小部件的 属性 设置为您在诱导小部件中引入的那个(通过诱导小部件的 kv-id)。通过这种方式,您可以让接收小部件直接跟踪 属性 或在 属性 更改时触发函数。

选项 2:使用 App.get_running_app()。root.ids(“在 Python”中)直接与接收小部件对话。

选项 1 的示例


from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import ObjectProperty, BooleanProperty

Builder.load_string('''
<RootWidget>:
  button1: b1
  button2: b2
  BoxLayout:
    orientation: 'horizontal'
    size: root.size
    Button:
      id: b1
      text: 'I am the normal one'
      disabled: b2.switch         # <----- track the property of the other ('special') button
    SpecialButton:
      id: b2
      text: 'I am the special one'
<SpecialButton>:
  on_release: root.clickityclick()
''')

class RootWidget(Widget):
  button1 = ObjectProperty(None)
  button2 = ObjectProperty(None)

class SpecialButton(Button):
  switch = BooleanProperty(False)    # <----- introduce a property which is then tracked by the other ('normal') button
  def clickityclick(self):
    self.switch = not self.switch    # <----- only change your own property

class MinimalExampleApp(App):
  def build(self):
    return RootWidget()

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

选项 2 的示例

from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import ObjectProperty

Builder.load_string('''
<RootWidget>:
  button1: b1
  button2: b2
  BoxLayout:
    orientation: 'horizontal'
    size: root.size
    Button:
      id: b1
      text: 'I am the normal one'
    SpecialButton:
      id: b2
      text: 'I am the special one'
<SpecialButton>:
  on_release: root.clickityclick()
''')

class RootWidget(Widget):
  button1 = ObjectProperty(None)
  button2 = ObjectProperty(None)

class SpecialButton(Button):
  def clickityclick(self):
    App.get_running_app().root.ids.b1.disabled = \
    not App.get_running_app().root.ids.b1.disabled  # <---- directly access the other ('normal') button

class MinimalExampleApp(App):
  def build(self):
    return RootWidget()

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