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