Anidando widgets en kivy

Estoy intentando crear una interfaz en Kivy y creo que hay algunas cosas fundamentales que no entiendo sobre los widgets personalizados y cómo jerarquizarlos, incluso después de leer el tutorial. Creo que tengo más de una mentalidad de html de modelo de caja, por lo que la forma en que los widgets están nesteds en las GUI nativas es algo extraño para mí.

Algunos antecedentes:

  1. Consulté esta entrada sobre cómo agregar un fondo (responde a la pregunta: “Cómo agregar una imagen de fondo / color / video / … a un diseño”, que creo que estaba replicando con los métodos _update_rect).

  2. Este que tiene un evento on_touch_down.

K, estoy tratando de hacer que MyApp se vea así …

introduzca la descripción de la imagen aquí

Como lo entiendo, aquí están las cosas que necesitaría para eso:

  1. Tienes una aplicación.
  2. La aplicación tiene una raíz.
  3. La raíz tiene un fondo, digamos que el fondo es blanco.
  4. El fondo contiene un soporte, digamos que el titular tiene un pequeño margen del fondo y es gris. Quiero que esto sea un widget en lugar de solo un canvas que no es de widgets porque quiero que el titular reaccione también al hacer clic en eventos. Vuelve colores aleatorios cuando se hace clic. (Sólo para demostrar que está haciendo algo.)
  5. El titular contiene dos widgets personalizados. Estos son círculos con tags, cada uno de color verde. Se vuelven colores aleatorios cuando se hace clic (solo para mostrar que están haciendo algo).

Aquí está mi código, que ya no falla, pero no muestra colores ni objetos de ningún tipo.

#!/usr/bin/kivy import kivy kivy.require('1.7.2') from random import random from kivy.app import App from kivy.uix.widget import Widget from kivy.uix.floatlayout import FloatLayout from kivy.graphics import Color, Ellipse, Rectangle class MyApp(App): title = 'My App' def build(self): root = RootWidget() root.bind( size=self._update_rect, pos=self._update_rect) def _update_rect(self, instance, value): self.rect.pos = instance.pos self.rect.size = instance.size class RootWidget(FloatLayout): def __init__(self, **kwargs): super(RootWidget, self).__init__(**kwargs) with self.canvas.before: Color(1, 0, 0, 1) # This RED does not display. self.rect = Rectangle( size=self.size, pos=self.pos, text="some junk!") # This label does not display. mybackground = Background() self.add_widget(mybackground) def _update_rect(self, instance, value): self.rect.pos = instance.pos self.rect.size = instance.size class Background(Widget): def __init__(self, **kwargs): super(Background, self).__init__(**kwargs) with self.canvas.before: Color(1, 1, 1, 1) # This WHITE does not display self.rect = Rectangle( size=self.size, pos=self.pos, text="More stuff!") # This label does not display. myholder = Holder() self.add_widget(myholder) def _update_rect(self, instance, value): self.rect.pos = instance.pos self.rect.size = instance.size class Holder(Widget): def __init__(self, **kwargs): super(Holder, self).__init__(**kwargs) with self.canvas.before: Color(0.25, 0.25, 0.25, 1) # This GRAY does not display self.rect = Rectangle( size=self.size, pos=self.pos, text="More stuff!") # This does not display. c1 = Circley(label="Label 1") # I see I'd need to do some size/pos math here to center c2 = Circley(label="Label 2") # but since everything isn't working, I've tabled this. self.add_widget(c1) self.add_widget(c2) def _update_rect(self, instance, value): self.rect.pos = instance.pos self.rect.size = instance.size def on_touch_down(self, touch): rcolor = Color(random(), random(), random(), 1) with self.canvas: self.color = rcolor class Circley(Widget): def __init__(self, label='label', **kwargs): super(Circley, self).__init__(**kwargs) with self.canvas.before: Color(0, 1, 0, 1) # This GREEN does not display self.circ = Ellipse( size=self.size, pos=self.pos, text=label ) def _update_circ(self, instance, value): self.circ.pos = instance.pos self.circ.size = instance.size def on_touch_down(self, touch): rcolor = Color(random(), random(), random(), 1) with self.canvas: self.color = rcolor if __name__ == '__main__': MyApp().run() 

¿Algún indicador sobre lo que estoy haciendo mal y cómo anidar estos widgets correctamente?

La razón por la que obtiene una pantalla en blanco es que el método build() su aplicación no devuelve nada. Lo que devuelva será el widget raíz, pero aunque crea algunos widgets, no devuelve uno, así que no se muestra nada.

Si solucionas esto, puedes ejecutar la aplicación de nuevo, pero inmediatamente obtendrás un error, algo así como que MyApp has no attribute rect . Esto se debe a que su widget de raíz se dimensiona y posiciona inmediatamente para llenar la ventana, lo que (según su línea root.bind ) activa MyApp._update_rect . Sin embargo, este método intenta modificar MyApp.rect.pos … ¡pero la aplicación no tiene un self.rect! Presumiblemente pretendías vincularlo a root._update_rect , no al método de la aplicación. (Edición: me he unido a root._update_rect en root._update_rect lugar y ahora al menos aparecen el fondo rojo y el círculo verde).

Edición: Y como nota al margen, sería mucho más fácil usar el lenguaje kv, que automáticamente se haría cargo de gran parte de la creación de widgets, el enlace de rectangularjs, etc.

No tengo tiempo para arreglarlo todo ahora, pero quizás estos dos problemas pueden ayudarlo a solucionar el flujo general. Intentaré publicar una respuesta más completa más tarde si nadie más lo ha hecho.


Aquí hay una MyApp actualizada, según los comentarios.

 class MyApp(App): title = 'My App' def build(self): root = RootWidget() root.bind( size=root._update_rect, pos=root._update_rect) 
 from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.uix.widget import Widget from kivy.graphics import Color, Rectangle, Ellipse class MyApp(App): title = 'My App' def build(self): root = RootWidget() root.bind( size=root._update_rect, pos=root._update_rect) return root class RootWidget(FloatLayout): def __init__(self, **kwargs): super(RootWidget, self).__init__(**kwargs) with self.canvas.before: Color(1, 0, 0, 1) # This RED does not display. self.rect = Rectangle( size=self.size, pos=self.pos, text="some junk!") # This label does not display. mybackground = Background() self.add_widget(mybackground) def _update_rect(self, instance, value): self.rect.pos = instance.pos self.rect.size = instance.size class Background(Widget): def __init__(self, **kwargs): super(Background, self).__init__(**kwargs) with self.canvas.before: Color(1, 1, 1, 1) # This WHITE does not display self.rect = Rectangle( size=self.size, pos=self.pos, text="More stuff!") # This label does not display. myholder = Holder() self.add_widget(myholder) def _update_rect(self, instance, value): self.rect.pos = instance.pos self.rect.size = instance.size class Holder(Widget): def __init__(self, **kwargs): super(Holder, self).__init__(**kwargs) with self.canvas.before: Color(0.25, 0.25, 0.25, 1) # This GRAY does not display self.rect = Rectangle( size=self.size, pos=self.pos, text="More stuff!") # This does not display. c1 = Circley(label="Label 1") # I see I'd need to do some size/pos math here to center c2 = Circley(label="Label 2") # but since everything isn't working, I've tabled this. self.add_widget(c1) self.add_widget(c2) def _update_rect(self, instance, value): self.rect.pos = instance.pos self.rect.size = instance.size def on_touch_down(self, touch): rcolor = Color(random(), random(), random(), 1) with self.canvas: self.color = rcolor class Circley(Widget): def __init__(self, label='label', **kwargs): super(Circley, self).__init__(**kwargs) with self.canvas.before: Color(0, 1, 0, 1) # This GREEN does not display self.circ = Ellipse( size=self.size, pos=self.pos, text=label ) def _update_circ(self, instance, value): self.circ.pos = instance.pos self.circ.size = instance.size def on_touch_down(self, touch): rcolor = Color(random(), random(), random(), 1) with self.canvas: self.color = rcolor if __name__ == '__main__': MyApp().run()