Kivy: cómo obtener un widget por id (sin kv)

Digamos que defino sobre la marcha en Kivy algunos widgets (botones) y asigno dinámicamente su ID. No estoy usando lenguaje kv en este caso de uso. Puedo mantener una referencia de un ID de widget sin hacer un seguimiento del propio widget: luego me gustaría acceder al widget a través de su ID. ¿Puedo hacer algo como “obtener widget por id”? (Si hubiera definido el widget en un archivo kv, podría haber usado self.ids.the_widget_id para acceder al propio widget a través de su ID)

Los widgets de Kivy hacen estructura de árbol. Los hijos de cualquier widget están disponibles a través del atributo children . Si lo desea, puede mantener la referencia solo a la ventana raíz y luego iterar sobre sus widgets utilizando el método de walk :

 from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button class MyWidget(BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) button = Button(text="...", id="1") button.bind(on_release=self.print_label) l1 = BoxLayout(id="2") l2 = BoxLayout(id="3") self.add_widget(l1) l1.add_widget(l2) l2.add_widget(button) def print_label(self, *args): for widget in self.walk(): print("{} -> {}".format(widget, widget.id)) class MyApp(App): def build(self): return MyWidget() if __name__ == '__main__': MyApp().run() 

walk() walk_reverse() walk() y walk_reverse() se agregaron a kivy.uix.widget.Widget en la versión 1.8.1 de Kivy. Para versiones anteriores necesitas analizar tu mismo de forma recursiva:

 from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button class MyWidget(BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) button = Button(text="...", id="1") button.bind(on_release=self.print_label) l1 = BoxLayout(id="2") l2 = BoxLayout(id="3") self.add_widget(l1) l1.add_widget(l2) l2.add_widget(button) def print_label(self, *args): children = self.children[:] while children: child = children.pop() print("{} -> {}".format(child, child.id)) children.extend(child.children) class MyApp(App): def build(self): return MyWidget() if __name__ == '__main__': MyApp().run() 

Puede recuperar el widget utilizando directamente los identificadores. Por ejemplo, en su código puede modificar el texto del botón con el siguiente fragmento de código:

 self.ids.2.ids.3.ids.1.text = '!!!!' 

Puedes cambiar las propiedades de cada widget usando IDs:

 self.ids['order_number'].text='S1212'