← Volver al listado de tecnologías
Widgets y Layouts
Jerarquía de Widgets
Widget (base)
├── Layout
│ ├── BoxLayout
│ ├── GridLayout
│ ├── FloatLayout
│ ├── RelativeLayout
│ ├── AnchorLayout
│ ├── StackLayout
│ └── PageLayout
└── Widgets de UI
├── Label, Button
├── TextInput
├── Image
├── Slider, Switch
└── ...
Layouts Principales
BoxLayout
Organiza widgets en fila o columna.
BoxLayout:
orientation: 'vertical' # o 'horizontal'
spacing: 10
padding: 20
Button:
text: 'Botón 1'
Button:
text: 'Botón 2'
Button:
text: 'Botón 3'
GridLayout
Organiza en cuadrícula.
GridLayout:
cols: 3 # Columnas fijas
rows: 2 # Filas fijas (opcional)
spacing: 5
padding: 10
Button:
text: '1'
Button:
text: '2'
Button:
text: '3'
Button:
text: '4'
FloatLayout
Posicionamiento libre con coordenadas.
FloatLayout:
Button:
text: 'Centro'
size_hint: 0.3, 0.2
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
Button:
text: 'Esquina'
size_hint: 0.2, 0.1
pos_hint: {'right': 1, 'top': 1}
AnchorLayout
Ancla widgets a posiciones específicas.
AnchorLayout:
anchor_x: 'center' # 'left', 'center', 'right'
anchor_y: 'center' # 'top', 'center', 'bottom'
Button:
text: 'Centrado'
size_hint: 0.5, 0.3
StackLayout
Apila widgets con wrap automático.
StackLayout:
orientation: 'lr-tb' # left-right, top-bottom
spacing: 5
Button:
text: 'A'
size_hint: 0.3, 0.2
Button:
text: 'B'
size_hint: 0.3, 0.2
# Continúa en siguiente fila si no cabe
RelativeLayout
Como FloatLayout pero posiciones relativas al padre.
RelativeLayout:
Button:
text: 'Relativo'
pos_hint: {'x': 0.1, 'y': 0.1}
size_hint: 0.3, 0.2
Size Hints
Controlan el tamaño relativo de widgets.
| Propiedad | Descripción |
|---|---|
size_hint | (ancho, alto) como fracción del padre |
size_hint_x | Solo ancho relativo |
size_hint_y | Solo alto relativo |
BoxLayout:
Button:
text: '30%'
size_hint_x: 0.3
Button:
text: '70%'
size_hint_x: 0.7
Tamaño Fijo
Button:
size_hint: None, None
size: 200, 50
# o
width: 200
height: 50
Pos Hints (FloatLayout)
| Clave | Descripción |
|---|---|
x, y | Posición desde esquina inferior izquierda |
right, top | Posición desde esquina superior derecha |
center_x, center_y | Centro del widget |
FloatLayout:
Button:
text: 'Superior Derecha'
size_hint: 0.3, 0.1
pos_hint: {'right': 1, 'top': 1}
Button:
text: 'Centro'
size_hint: 0.4, 0.2
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
Button:
text: 'Inferior Izquierda'
size_hint: 0.3, 0.1
pos_hint: {'x': 0, 'y': 0}
Widgets de Entrada
TextInput
TextInput:
hint_text: 'Placeholder'
multiline: False
password: True
input_filter: 'int' # Solo números
max_chars: 10
Slider
BoxLayout:
orientation: 'vertical'
Slider:
id: slider
min: 0
max: 100
value: 50
step: 1
Label:
text: str(int(slider.value))
Switch
BoxLayout:
Switch:
id: switch
active: True
Label:
text: 'ON' if switch.active else 'OFF'
Spinner (Dropdown)
Spinner:
text: 'Selecciona'
values: ['Opción 1', 'Opción 2', 'Opción 3']
on_text: print(self.text)
CheckBox
BoxLayout:
CheckBox:
id: check
active: False
Label:
text: 'Acepto términos' if check.active else 'No acepto'
Widgets de Visualización
Image
Image:
source: 'ruta/imagen.png'
allow_stretch: True
keep_ratio: True
AsyncImage (carga remota)
AsyncImage:
source: 'https://ejemplo.com/imagen.jpg'
ProgressBar
ProgressBar:
max: 100
value: 75
Widgets Contenedores
ScrollView
ScrollView:
do_scroll_x: False
do_scroll_y: True
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height
# Widgets que exceden la pantalla
Button:
text: 'Item 1'
size_hint_y: None
height: 50
TabbedPanel
TabbedPanel:
do_default_tab: False
TabbedPanelItem:
text: 'Tab 1'
BoxLayout:
Label:
text: 'Contenido Tab 1'
TabbedPanelItem:
text: 'Tab 2'
BoxLayout:
Label:
text: 'Contenido Tab 2'
Accordion
Accordion:
orientation: 'vertical'
AccordionItem:
title: 'Sección 1'
Label:
text: 'Contenido 1'
AccordionItem:
title: 'Sección 2'
Label:
text: 'Contenido 2'
Popup
from kivy.uix.popup import Popup
from kivy.uix.label import Label
popup = Popup(
title='Alerta',
content=Label(text='Mensaje'),
size_hint=(0.8, 0.4)
)
popup.open()
Widgets Personalizados
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class Card(BoxLayout):
titulo = StringProperty('')
descripcion = StringProperty('')
<Card>:
orientation: 'vertical'
padding: 10
spacing: 5
canvas.before:
Color:
rgba: 0.2, 0.2, 0.2, 1
RoundedRectangle:
pos: self.pos
size: self.size
radius: [10]
Label:
text: root.titulo
font_size: '18sp'
bold: True
size_hint_y: 0.3
Label:
text: root.descripcion
font_size: '14sp'
size_hint_y: 0.7
Testing de Layouts
# test_layouts.py
import unittest
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
class TestLayouts(unittest.TestCase):
def test_boxlayout_orientacion(self):
box = BoxLayout(orientation='vertical')
self.assertEqual(box.orientation, 'vertical')
def test_gridlayout_columnas(self):
grid = GridLayout(cols=3)
for i in range(6):
grid.add_widget(Button(text=str(i)))
self.assertEqual(len(grid.children), 6)
def test_size_hint(self):
btn = Button(size_hint=(0.5, 0.5))
self.assertEqual(btn.size_hint, (0.5, 0.5))
def test_add_remove_widget(self):
box = BoxLayout()
btn = Button()
box.add_widget(btn)
self.assertIn(btn, box.children)
box.remove_widget(btn)
self.assertNotIn(btn, box.children)
if __name__ == '__main__':
unittest.main()
Probar en Dispositivo con ADB
# Ver jerarquía de views (útil para debug de layouts)
adb shell dumpsys activity top | grep -A 20 "View Hierarchy"
# Activar mostrar límites de layout en dispositivo
adb shell setprop debug.layout true
adb shell service call activity 1599295570
# Capturar pantalla para verificar layout
adb exec-out screencap -p > layout_test.png
# Ver rendimiento de UI
adb shell dumpsys gfxinfo com.ejemplo.miapp
Resumen
- BoxLayout: Fila o columna
- GridLayout: Cuadrícula con cols/rows
- FloatLayout: Posicionamiento libre
- size_hint: Tamaño relativo (0.0 - 1.0)
- pos_hint: Posición relativa en FloatLayout
- ScrollView: Contenido scrolleable