在 kivy lang 和 python 中访问子小部件属性的值

2022-01-15 00:00:00 python python-3.x kivy kivy-language widget

问题描述

  1. 如何覆盖 Kivy 小部件子部件的默认值?即 MyWidget.label 是默认",但我想将其更改为例如当 MyRootWidget 的孩子时出现紫龟"?

  1. How can I overwrite the default value of a Kivy widget's child? i.e. MyWidget.label is 'default' but I want to change it to e.g. 'purple turtle' when a child of MyRootWidget?

我可以像在MyRootWidget.__init__()中那样访问children的children,但是看起来很麻烦,尤其是对于一棵很深的树...有没有更优雅的怎么办?

I can access the children of children as I've done in MyRootWidget.__init__(), but it seems cumbersome, especially for a deep tree ... is there a more elegant way of doing it?

我一直在查看 Kivy lang 和 Widget 页面,但无法理解是否存在解决方案.我在 SO 页面中还没有看到这个问题(尽管在我搜索时确实回答了一个不同的问题).

I've been looking over the Kivy lang and Widget pages, but haven't been able to understand a solution if it's there. I haven't seen quite this issue in SO pages (though the did answer a different question while I was searching).

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, StringProperty


root = Builder.load_string('''
<MyWidget@BoxLayout>:
    orientation: 'vertical'
    Label:
        id: label
        text: 'DEFAULT'

<MyRootWidget@BoxLayout>:
    id: root_widget
    MyWidget:
        id: w1
        # (---1---) 
''')


class MyRootWidget(BoxLayout):
    w1 = ObjectProperty()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        print(self.ids.w1.ids.label.text)  # (---2---)


class MainApp(App):
    def build(self):
        return MyRootWidget()


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


解决方案

首先,在实现任何代码之前,你必须设计你的类.

First of all, before implementing any code, you must design your classes.

首先我们将使用 MyWidget,在您的要求中表明您希望文本是可修改的,因此它必须是根属性.

First we will do it with MyWidget, in your requirements you indicate that you want the text to be modifiable so it must be a root property.

MyWidget
┌--------------------------┐
|                          |
| ┌-------------┐   text---┼--->
| | Label       |     |    |
| |    *text ---┼-----┘    |
| └-------------┘          |
└--------------------------┘

MyRootWidget 也可以这样做:

The same could be done with MyRootWidget:

MyRootWidget
┌-----------------------------┐
|                             |
| ┌-------------┐ obj_widget--┼--->
| | MyWidget  --┼-----┘       |
| |             |             |
| └-------------┘             |
└-----------------------------┘

属性的深度取决于您的要求,但如果我们观察到公开属性,则意味着在根中创建属性并进行绑定,以便在修改根属性时也会修改内部属性.

The depth of the property depends on your requirements, but if we observe exposing a property it implies creating a property in the root and making a binding so that when the root property is modified the internal property is modified as well.

实现上述你会得到以下结果:

Implementing the above you get the following:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, StringProperty

root = Builder.load_string('''
<MyWidget@BoxLayout>:
    text: "DEFAULT"
    obj_label: label
    orientation: 'vertical'
    Label:
        id: label
        text: root.text

<MyRootWidget@BoxLayout>:
    obj_widget: w1
    MyWidget:
        id: w1
        text: "purple turtle"
''')

class MyRootWidget(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        print(self.obj_widget.text) 

class MainApp(App):
    def build(self):
        return MyRootWidget()

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

因此,为了避免 id,您可以为子小部件创建一个别名,就像我对 obj_widget 所做的那样,它是 w1 的别名.

So to avoid the ids you can create an alias to the children widget like I did with obj_widget which is an alias of w1.

按照设计,您不应该直接访问完整的树,但您必须修改层的属性,如果修改了该层,则必须更新其内部的必要数据,以避免类之间的耦合.

By design you should not access the complete tree directly but you must modify the property of a layer and this layer if it is modified then you must update the necessary data in its internal part so we avoid the coupling between classes.

相关文章