16. Interfaces de usuario en Jupyter con ipywidgets
#
Jupyter y IPython permiten no sólo visualizar datos sino también interactuar con ellos en tiempo real
Para lograr esto estudiaremos los componentes de la librería ipywidgets
Advertencia
Los callbacks de los widgets requieren conexión con un intérprete de IPython. Si estás viendo este cuadernillo desde phuijse.github.io/PythonBook entonces los widgets no se visualizarán correctamente
16.1. ¿Qué es un widget?#
Un widget es un elemento gráfico que permite interactuar con una aplicación
Un conjunto de widgets forman una interfaz de usuario gráfica (Graphical user interface o GUI)
La siguiente imagen muestra los widgets típicos que se encuentran en las aplicaciones de PC
La librería ipywidgets
provee controles que permiten interactuar con funciones de Python
Entre los muchos widgets disponibles se encuentran:
Botones e selectores (check-box)
Listas desplegables (combo-box)
Campos de ingreso de texto (text-box)
Deslizadores (sliders)
Instalación
La forma más sencilla de instalar es usando conda
conda install ipywidgets
Si lo instalas de otra manera (pip o python setup.py) es necesario habilitar la extensión
pip install ipywidgets --user
jupyter nbextension enable --py widgetsnbextension
16.2. Creación semi-automática de widgets#
Una manera rápida de implementar widgets es usar el decorador interact
Al aplicar el decorador a una función convertiremos sus argumentos de entrada en widgets
Cada entrada genera y se enlaza a un widget según su tipo:
Un entero produce un widget
IntSlider
Un flotante produce un widget
FloatSlider
Un booleando produce un widget
Checkbox
Una lista produce un widget
Dropdown
Un string produce un widget
Text
Los argumentos del decorador permiten entregar algunas opciones a los widgets
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import ipywidgets as widgets
# Se crea un widget por cada argumento de la función
@widgets.interact(x=(0, 10, 2), # El mínimo, máximo y paso para x
y=(-1., 1., 0.01) # El mínimo, máximo y paso para y
)
def print_cosas(x=0,
y=0.,
z=True,
w=['foo','bar'],
v='foo'):
display(x, y, z, w, v)
16.3. Creación manual de widgets#
Para mayor control podemos crear los widgets de nuestra preferencia con sus respectivos constructores y luego enlazarlos a una función usando manualmente con la función interact
A continuación revisaremos algunos de ellos
16.3.1. Widgets numéricos#
Un desplazador o slider es un widget cuyos argumentos son
min: valor donde inicia
max: valor donde termina
step: el salto entre valores
value: valor inicial
El slider generará números entre esos rangos a medida que interactuamos con él
Se puede crear un desplazador que produce números enteros con IntSlider
o flotantes con FloatSlider
Ejemplo
f = lambda a: display(a)
x = widgets.IntSlider(min=-100, max=100, step=5) # Esto crea el widget
widgets.interact(f, a=x) # Esto enlaza x con a para la función f
<function __main__.<lambda>(a)>
También se pueden generar tuplas de enteros y flotantes usando IntRangeSlider
y FloatRangeSlider
, respectivamente
Por otro lado los widgets te tipo text-box FloatText
y IntText
pueden usarse para pedir un número al usuario
f = lambda a: display(a)
x = widgets.FloatText(description="Por favor ingrese un número")
widgets.interact(f, a=x) # Esto enlaza el widget al primer argumento de f
En ambos casos podemos obtener el valor de los widgets usando
x.value
Podemos verificar todos los atributos del widget usando
x.keys
16.3.2. Widgets de texto#
Sirven para capturar y mostrar strings
Text
: Da una linea en blanco para escribir, al apretar Enter se captura el stringTextarea
: Da un bloque de texto para escribir, se comporta igual aText
Label
: Muestra un string
widgets.Text(value, # Texto por defecto
placeholder, # Texto que aparece cuando está vacío
description # Texto que aparece a la izquierda del cuadrado de texto
)
Ejemplo
widgets.Text(placeholder='Escribe tu nombre acá', description='Nombre:')
16.3.3. Objeto Layout y atributo style#
El objeto Layout
sirve para dar estilo a los widgets
El estilo se explicita usando CSS
Algunos argumentos útiles son
width: ancho del widget en pixeles
height: alto del widget en pixeles
margin: espacio entre el widget y otros componentes
Cada widget tiene un atributo llamado style
para personalizarlo
La lista de atributos de estilo se puede acceder con
x = widgets.Button()
x.style.keys
Ejemplo
slider_layout = widgets.Layout(width='600px', height='20px', margin='10px')
def mi_funcion(x):
display(x[0], x[1], (x[1] - x[0]))
range_slider = widgets.FloatRangeSlider(min=-100., max=100., step=0.01,
continuous_update=True,
description=r'Un argumento muy interesante:',
layout=slider_layout)
range_slider.style.description_width = 'initial'
range_slider.style.handle_color = 'black'
widgets.interact(mi_funcion, x=range_slider);
16.3.4. Widget de tipo contenedor#
Son widgets que sirven para organizar otros widgets
Por ejemplo
HBox
: Organiza los widgets en forma horizontalVBox
: Organiza los widgets en forma verticalTab
: Crea pestañas con los widgets
Estos widgets reciben una lista de widgets y pueden anidarse
Ejemplo
firstname = widgets.Text(description='Nombre:')
lastname = widgets.Text(description='Apellido:')
widgets.VBox([firstname, lastname])
16.4. Callbacks y eventos#
16.4.2. Atributo observe
#
Podemos agregar acciones a otros widgets con el método observe
Importante
observe
recibe una función de un argumento y el nombre del atributo que queremos “observar”
Cada vez que haya un cambio en el atributo se llamará la función que especificamos
El argumento de la función es un diccionario que tiene las siguientes llaves
owner
: El widget que provocó el cambioname
: El nombre del atributo que está cambiandoold
: El valor antiguo del atributonew
: El valor nuevo del atributo
Ejemplo
def handler(change):
# change es un diccionario para el atributo value
display(f"Hola {change['new']}, Chao {change['old']}")
sel_slider = widgets.Dropdown(description="Nombre:",
options=["Pablo", "Eliana", "Hector"])
display(sel_slider)
sel_slider.observe(handler, names='value')
16.4.3. widget Output#
Este es un widget especial que puede usarse para redireccionar las salidas de otros widgets
Primero creamos y mostrarmos Output
Luego lo usamos de contexto para la salida de otro widget
Ejemplo
out = widgets.Output(layout={'border': '1px solid black'})
display(out)
def on_change(button):
with out:
display(insert_name.value) # Esto se va escribir donde quiera que esté out
insert_name = widgets.Text(description='Escribir tu nombre')
insert_name.style.description_width = 'initial'
push_data = widgets.Button(description='Enviar')
display(widgets.HBox([insert_name, push_data]))
push_data.on_click(on_change)