← Volver al listado de tecnologías

Primera Aplicación

Por: SiempreListo
kivypythonappwidgets

La Clase App

Toda aplicación Kivy hereda de kivy.app.App. El método build() retorna el widget raíz.

Hello World

# main.py
from kivy.app import App
from kivy.uix.label import Label

class MiApp(App):
    def build(self):
        return Label(text='¡Hola Kivy!')

if __name__ == '__main__':
    MiApp().run()

Ciclo de Vida de la Aplicación

┌──────────────┐
│   __init__   │  Inicialización
└──────┬───────┘

┌──────────────┐
│    build     │  Construir UI (retorna widget raíz)
└──────┬───────┘

┌──────────────┐
│  on_start    │  App lista, antes de mostrar
└──────┬───────┘

┌──────────────┐
│    run()     │  Loop principal
└──────┬───────┘

┌──────────────┐
│   on_pause   │  (Móvil) App en segundo plano
└──────┬───────┘

┌──────────────┐
│  on_resume   │  (Móvil) App regresa a primer plano
└──────┬───────┘

┌──────────────┐
│   on_stop    │  App terminando
└──────────────┘

Métodos del Ciclo de Vida

from kivy.app import App
from kivy.uix.label import Label

class CicloApp(App):
    def build(self):
        print("build: Construyendo UI")
        return Label(text='Ciclo de Vida')

    def on_start(self):
        print("on_start: App iniciada")

    def on_pause(self):
        print("on_pause: App pausada")
        return True  # Permite pausar

    def on_resume(self):
        print("on_resume: App resumida")

    def on_stop(self):
        print("on_stop: App terminando")

if __name__ == '__main__':
    CicloApp().run()

Widgets Básicos

Label (Texto)

from kivy.uix.label import Label

label = Label(
    text='Texto de ejemplo',
    font_size='24sp',
    color=(1, 0, 0, 1),  # RGBA: Rojo
    halign='center',
    valign='middle'
)

Button (Botón)

from kivy.uix.button import Button

def on_press(instance):
    print(f'Botón presionado: {instance.text}')

boton = Button(
    text='Presióname',
    font_size='20sp',
    on_press=on_press
)

TextInput (Campo de texto)

from kivy.uix.textinput import TextInput

entrada = TextInput(
    text='',
    hint_text='Escribe aquí...',
    multiline=False
)

Aplicación con Interacción

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput

class SaludoApp(App):
    def build(self):
        # Layout vertical
        self.layout = BoxLayout(orientation='vertical', padding=20, spacing=10)

        # Widgets
        self.entrada = TextInput(
            hint_text='Tu nombre',
            multiline=False,
            size_hint_y=None,
            height=50
        )

        self.boton = Button(
            text='Saludar',
            size_hint_y=None,
            height=50
        )
        self.boton.bind(on_press=self.saludar)

        self.label = Label(text='', font_size='24sp')

        # Agregar al layout
        self.layout.add_widget(self.entrada)
        self.layout.add_widget(self.boton)
        self.layout.add_widget(self.label)

        return self.layout

    def saludar(self, instance):
        nombre = self.entrada.text or 'Mundo'
        self.label.text = f'¡Hola, {nombre}!'

if __name__ == '__main__':
    SaludoApp().run()

Propiedades de Ventana

from kivy.app import App
from kivy.core.window import Window
from kivy.uix.label import Label

# Configurar antes de crear widgets
Window.size = (400, 600)
Window.clearcolor = (0.1, 0.1, 0.1, 1)  # Fondo oscuro

class VentanaApp(App):
    def build(self):
        return Label(text='Ventana personalizada')

if __name__ == '__main__':
    VentanaApp().run()

Propiedades de Window

PropiedadDescripción
sizeTamaño (ancho, alto)
clearcolorColor de fondo RGBA
fullscreenModo pantalla completa
borderlessSin bordes
top, leftPosición de ventana

Manejo de Eventos de Ventana

from kivy.app import App
from kivy.core.window import Window
from kivy.uix.label import Label

class EventosApp(App):
    def build(self):
        Window.bind(on_key_down=self.on_tecla)
        Window.bind(on_resize=self.on_resize)
        self.label = Label(text='Presiona teclas o redimensiona')
        return self.label

    def on_tecla(self, window, key, scancode, codepoint, modifier):
        self.label.text = f'Tecla: {key}'
        if key == 27:  # ESC
            return True  # Previene cerrar
        return False

    def on_resize(self, window, width, height):
        self.label.text = f'Tamaño: {width}x{height}'

if __name__ == '__main__':
    EventosApp().run()

Convención de Nombres

El archivo .kv se asocia automáticamente por nombre:

Clase AppArchivo KV
MiAppmi.kv
SaludoAppsaludo.kv
TestApptest.kv

La regla: nombre de clase sin “App” en minúsculas.

Testing del Ciclo de Vida

# test_app.py
import unittest
from kivy.app import App
from kivy.uix.label import Label

class MiApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.eventos = []

    def build(self):
        self.eventos.append('build')
        return Label(text='Test')

    def on_start(self):
        self.eventos.append('on_start')
        self.stop()  # Detener para test

class TestCicloVida(unittest.TestCase):
    def test_ciclo_vida_app(self):
        app = MiApp()
        app.run()
        self.assertIn('build', app.eventos)
        self.assertIn('on_start', app.eventos)

    def test_label_texto(self):
        label = Label(text='Hola')
        self.assertEqual(label.text, 'Hola')

if __name__ == '__main__':
    unittest.main()

Probar en Dispositivo con ADB

# Compilar e instalar
buildozer android debug deploy

# Ver logs específicos de tu app
adb logcat -s python:V

# Monitorear uso de memoria
adb shell dumpsys meminfo com.ejemplo.miapp

# Verificar que la app inició correctamente
adb shell ps | grep miapp

Resumen