← Volver al listado de tecnologías
Canvas y Gráficos
Sistema de Canvas
Cada widget tiene un canvas para dibujar. Tres capas disponibles:
canvas.before → Dibuja primero (fondo)
canvas → Capa principal
canvas.after → Dibuja último (overlay)
Instrucciones de Color
Widget:
canvas:
Color:
rgba: 1, 0, 0, 1 # Rojo opaco
# rgb: 1, 0, 0 # Sin alpha
# hsv: 0, 1, 1 # HSV
from kivy.graphics import Color
with self.canvas:
Color(1, 0, 0, 1) # RGBA
Color(rgb=(0, 1, 0))
Color(hsv=(0.5, 1, 1))
Formas Básicas
Rectangle
Widget:
canvas:
Color:
rgba: 0.3, 0.6, 0.9, 1
Rectangle:
pos: self.pos
size: self.size
RoundedRectangle
canvas:
RoundedRectangle:
pos: self.pos
size: self.size
radius: [20, 20, 20, 20] # Esquinas
Ellipse
canvas:
Ellipse:
pos: self.pos
size: self.size
# Arco parcial
angle_start: 0
angle_end: 180
Line
canvas:
Line:
points: [100, 100, 200, 200, 300, 100]
width: 2
close: True # Cerrar forma
Triangle
canvas:
Triangle:
points: [100, 100, 200, 300, 300, 100]
Instrucciones en Python
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle
class MiCanvas(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Color(1, 0, 0, 1)
self.rect = Rectangle(pos=self.pos, size=self.size)
# Actualizar cuando cambie tamaño/posición
self.bind(pos=self.actualizar, size=self.actualizar)
def actualizar(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
Texturas e Imágenes
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'imagen.png'
from kivy.graphics import Rectangle
with self.canvas:
Rectangle(
pos=self.pos,
size=self.size,
source='imagen.png'
)
Transformaciones
Translate (Mover)
canvas:
PushMatrix:
Translate:
x: 50
y: 50
Rectangle:
size: 100, 100
PopMatrix:
Rotate (Rotar)
canvas:
PushMatrix:
Rotate:
angle: 45
origin: self.center
Rectangle:
pos: self.pos
size: self.size
PopMatrix:
Scale (Escalar)
canvas:
PushMatrix:
Scale:
x: 2
y: 2
origin: self.center
Rectangle:
pos: self.pos
size: self.size
PopMatrix:
Ejemplo: Widget con Fondo
<CardWidget>:
canvas.before:
# Sombra
Color:
rgba: 0, 0, 0, 0.3
RoundedRectangle:
pos: self.x + 3, self.y - 3
size: self.size
radius: [10]
# Fondo
Color:
rgba: 1, 1, 1, 1
RoundedRectangle:
pos: self.pos
size: self.size
radius: [10]
canvas.after:
# Borde
Color:
rgba: 0.8, 0.8, 0.8, 1
Line:
rounded_rectangle: self.x, self.y, self.width, self.height, 10
width: 1
Dibujo Dinámico
from kivy.uix.widget import Widget
from kivy.graphics import Color, Line
class Pizarra(Widget):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
with self.canvas:
Color(1, 1, 1, 1)
touch.ud['linea'] = Line(points=[touch.x, touch.y], width=2)
return True
return super().on_touch_down(touch)
def on_touch_move(self, touch):
if 'linea' in touch.ud:
touch.ud['linea'].points += [touch.x, touch.y]
return True
return super().on_touch_move(touch)
def limpiar(self):
self.canvas.clear()
Instrucciones Avanzadas
Bezier
canvas:
Bezier:
points: [0, 0, 100, 200, 200, 200, 300, 0]
segments: 50
Mesh
canvas:
Mesh:
vertices: [0, 0, 0, 0, 100, 0, 0, 0, 100, 100, 0, 0]
indices: [0, 1, 2]
mode: 'triangle_fan'
SmoothLine
canvas:
SmoothLine:
points: self.puntos
width: 2
Gradientes
from kivy.graphics import Rectangle
from kivy.graphics.texture import Texture
def crear_gradiente(self):
texture = Texture.create(size=(2, 1))
# Gradiente horizontal rojo a azul
buf = bytes([255, 0, 0, 255, 0, 0, 255, 255])
texture.blit_buffer(buf, colorfmt='rgba', bufferfmt='ubyte')
texture.wrap = 'repeat'
with self.canvas:
Rectangle(texture=texture, pos=self.pos, size=self.size)
StencilView (Máscara)
StencilView:
size_hint: None, None
size: 200, 200
pos: 100, 100
Image:
source: 'imagen.png'
size: 400, 400
# Solo se ve dentro del StencilView
Ejemplo: Gráfico de Barras
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle
from kivy.properties import ListProperty
class BarChart(Widget):
datos = ListProperty([30, 50, 80, 40, 90])
colores = ListProperty([
(0.9, 0.3, 0.3, 1),
(0.3, 0.9, 0.3, 1),
(0.3, 0.3, 0.9, 1),
(0.9, 0.9, 0.3, 1),
(0.9, 0.3, 0.9, 1),
])
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bind(pos=self.dibujar, size=self.dibujar, datos=self.dibujar)
def dibujar(self, *args):
self.canvas.clear()
if not self.datos:
return
ancho_barra = self.width / len(self.datos)
max_valor = max(self.datos)
with self.canvas:
for i, valor in enumerate(self.datos):
Color(*self.colores[i % len(self.colores)])
altura = (valor / max_valor) * self.height
Rectangle(
pos=(self.x + i * ancho_barra + 5, self.y),
size=(ancho_barra - 10, altura)
)
Testing de Canvas
# test_canvas.py
import unittest
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle, Ellipse
class TestCanvas(unittest.TestCase):
def test_canvas_instrucciones(self):
w = Widget()
with w.canvas:
Color(1, 0, 0, 1)
rect = Rectangle(pos=(0, 0), size=(100, 100))
self.assertIsNotNone(rect)
self.assertEqual(rect.size, (100, 100))
def test_canvas_before_after(self):
w = Widget()
with w.canvas.before:
Color(0, 1, 0, 1)
with w.canvas.after:
Color(0, 0, 1, 1)
self.assertTrue(len(w.canvas.before.children) > 0)
self.assertTrue(len(w.canvas.after.children) > 0)
def test_modificar_instruccion(self):
w = Widget()
with w.canvas:
rect = Rectangle(size=(50, 50))
rect.size = (100, 100)
self.assertEqual(rect.size, (100, 100))
if __name__ == '__main__':
unittest.main()
Probar en Dispositivo con ADB
# Activar modo de depuración GPU
adb shell setprop debug.hwui.overdraw show
# Ver estadísticas de renderizado
adb shell dumpsys gfxinfo com.ejemplo.miapp framestats
# Capturar frame para análisis
adb shell screencap /sdcard/frame.png
adb pull /sdcard/frame.png
# Perfilar rendimiento OpenGL
adb shell setprop debug.egl.trace systrace
Resumen
canvas.before/aftercontrolan orden de dibujoColorafecta todas las instrucciones siguientesPushMatrix/PopMatrixaíslan transformacionesbind()actualiza gráficos cuando cambian propiedades- Las instrucciones son objetos que puedes modificar después