3
  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?

  2. 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?

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()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Daniel B.
  • 1,254
  • 1
  • 16
  • 35

1 Answers1

1

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

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 ---┼-----┘    |
| └-------------┘          |
└--------------------------┘

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()

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.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • So in order to modify any property in a widget, the property must be a root property? What if this goes several layers of children deep, do I need to "chain" a property across multiple layers to get the property to the widget that needs access to it, or is there some shortcut where a parent can access properties of a great grandchild? (in both directions, e.g. a widget updates a label in a great grandchild, and a great grandchild TextInput updates a label in the great grandparent.) – Daniel B. Nov 28 '18 at 05:17
  • @DanielB. You are seeing it as layers and you are missing the concept of a class: the class is something, it is good for something. For example, if you are going to create a class that is a widget for the login, let's call this Login class, so the right thing is that from the outside you can see the properties of username and password, the developer that is going to use your class is not interested internal classes such as TextInput that use that class. It is like a black box that has inputs and outputs. Therefore, there is no general response, it depends on what the class will serve. – eyllanesc Nov 28 '18 at 05:23
  • @DanielB. I recommend you to take the time and design a UML diagram of your classes, the properties represent the static characteristics of the objects that belong to the class, and the functions is the dynamic part that changes that static property. Let's go to a trivial example, we create the ball class, it will have like properties the size, the position, speed, etc and the methods move, setSize, setVelocity, etc. Internally to draw the ball you could use an Image but it is necessary to access Image from outside ?, no, it is not necessary. – eyllanesc Nov 28 '18 at 05:28
  • @DanielB. In conclusion focus to what your class (Widget) and establish its representative properties, then in the implementation internally connects the properties of the internal elements with the representative properties. The number of layers are indifferent. When you implement a widget do not think about your project, just think about the objective of that class, also think that that class you could use in another project, make it universal. – eyllanesc Nov 28 '18 at 05:31