Tutorial 2 — Hacerlo interesante¶
En Tutorial 1, generamos un esbozo del proyecto que era capaz de ejecutarse, pero no escribimos ningún código nosotros mismos. Echemos un vistazo a lo que se generó para nosotros.
Qué se generó¶
En el directorio src/helloworld, deberías ver 3 archivos: __init__.py,
__main__.py y app.py.
__init__.py marca el directorio helloworld como un módulo importable de
Python. Es un archivo vacío; el mero hecho de que exista le dice al intérprete
de Python que el directorio helloworld define un módulo.
__main__.py marca el módulo helloworld como una familia especial del módulo
—un módulo ejecutable. Si intentas ejecutar el módulo helloworld usando
python -m helloworld, el archivo __main__.py es donde Python empezará a
ejecutarse. El contenido de __main__.py es relativamente simple:
from helloworld.app import main
if __name__ == "__main__":
main().main_loop()
Este archivo hace dos cosas:
- Importa el método
maindesde la aplicaciónhelloworld. - A continuación, se inicia el bucle principal de la aplicación. El bucle principal es la forma en que una aplicación con interfaz gráfica de usuario (IGU) detecta la entrada del usuario (como pulsaciones del ratón y del teclado).
El archivo más interesante es app.py — contiene la lógica que crea la ventana
de nuestra aplicación:
import toga
from toga.style.pack import COLUMN, ROW
class HelloWorld(toga.App):
def startup(self):
main_box = toga.Box()
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
def main():
return HelloWorld()
Vamos a ir a través de esto línea por línea:
import toga
from toga.style.pack import COLUMNA, FILA
Primero, importamos el conjunto de herramientas de widgets toga, así como
algunas clases de utilidades y constantes relacionadas con el estilo. Nuestro
código aún no las utiliza, pero lo haremos en breve.
A continuación, definimos una clase:
class HelloWorld(toga.App):
Cada aplicación Toga tiene una única instancia toga.App, que representa la
entidad en ejecución que es la aplicación. La app puede acabar gestionando
múltiples ventanas; pero para aplicaciones sencillas, habrá una única ventana
principal.
A continuación, definimos un método startup():
def startup(self):
main_box = toga.Box()
Lo primero que hace el método de inicio startup() es definir una caja principal. El esquema de diseño de Toga se comporta de forma similar a HTML. Construyes una aplicación construyendo una colección de cajas, cada una de las cuales contiene otras cajas, o los widget actuales. Luego aplicas estilos a estas cajas para definir como consumirán el espacio disponible en la ventana.
En esta aplicación, definimos una sola caja, pero no ponemos nada dentro.
A continuación, definimos una ventana en la que podemos poner esta caja vacía:
self.main_window = toga.MainWindow(title=self.formal_name)
Esto crea una instancia de un toga.MainWindow, lo cual tendrá un título que
coincida con el nombre de la aplicación. Una Ventana Principal es un tipo
especial de ventana en Toga —es una ventana que está estrechamente vinculada al
ciclo de vida de la aplicación. Cuando se cierra la Ventana Principal, la
aplicación sale. La Ventana Principal es también la ventana que tiene el menú de
la aplicación (si estás en una plataforma como Windows donde las barras de menú
son parte de la ventana).
¿Dónde está mi ventana?
Si has cometido un error en tu código, es posible que la ventana principal de la aplicación no se exhiba. Si esto ocurre, puedes teclear Ctrl+C en el terminal donde iniciaste la aplicación. Esto detendrá la aplicación. Entonces puede corregir el error y reiniciar la aplicación.
A continuación añadimos nuestra caja vacía como el contenido de la ventana principal, e indicamos a la aplicación que muestre nuestra ventana:
self.main_window.content = main_box
self.main_window.show()
Por último, definimos una funciónmain(). Esto es lo que crea la instancia de
nuestra aplicación:
def main():
return HelloWorld()
Este método main() es el que es importado e invocado por __main__.py. Crea y
devuelve una instancia de nuestra aplicación HelloWorld.
Esa es la aplicación Toga más simple posible. Pongamos algo de nuestro propio contenido en la aplicación, y hagamos que la aplicación haga algo interesante.
Añadir algún contenido propio¶
Hagamos algo más interesante con nuestra aplicación HelloWorld.
Nota
Cuando realiza estos cambios, asegúrese que conserva las importaciones en la
parte superior del archivo, y el main() en la parte inferior del archivo. Solo
necesitas actualizar la clase HelloWorld.
Modifica tu clase HelloWorld dentro de src/helloworld/app.py para que tenga
este aspecto:
class HelloWorld(toga.App):
def startup(self):
main_box = toga.Box(direction=COLUMN)
name_label = toga.Label(
"Tu nombre: ",
margin=(0, 5),
)
self.name_input = toga.TextInput(flex=1)
name_box = toga.Box(direction=ROW, margin=5)
name_box.add(name_label)
name_box.add(self.name_input)
button = toga.Button(
"¡Hola!",
on_press=self.say_hello,
margin=5,
)
main_box.add(name_box)
main_box.add(button)
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
def say_hello(self, widget):
print(f"Hola, {self.name_input.value}")
Veamos en detalle lo que ha cambiado.
Seguimos creando una caja principal; sin embargo, ahora estamos aplicando un estilo:
main_box = toga.Box(direction=COLUMN)
El sistema de diseño integrado de Toga se llama "Pack". Se comporta de forma muy
parecida a CSS. Defines objetos en una jerarquía —en HTML, los objetos son
<div>, <span>, y otros elementos DOM—; en Toga, son widgets y cajas. A
continuación, puedes asignar estilos a los elementos individuales. En este caso,
estamos indicando que se trata de una caja COLUMN — es decir, es una caja que
consumirá todo el ancho disponible—, y ampliará su altura a medida que se añada
contenido, pero intentará ser lo más corta posible.
Nota
Para usos más avanzados, Toga también admite un objeto de estilo independiente, el cual se utiliza así:
from toga.style import Pack
main_box = toga.Box(style=Pack(direction=COLUMN))
A continuación, definimos un par de widgets:
name_label = toga.Label(
"Tu nombre: ",
margin=(0, 5),
)
self.name_input = toga.TextInput(flex=1)
Aquí definimos un Label y un TextInput. Ambos widgets tienen estilos asociados; la etiqueta tendrá 5px de margen a su izquierda y derecha, y ningún margen en la parte superior e inferior. El TextInput está marcado como flexible —es decir, absorberá todo el espacio disponible en su eje de diseño—.
El TextInput se asigna como una variable de instancia de la clase. Esto nos proporciona acceso fácil a la instancia del widget —algo que usaremos en un momento—.
A continuación, definimos una caja para alojar estos dos widgets:
name_box = toga.Box(direction=ROW, margin=5)
name_box.add(name_label)
name_box.add(self.name_input)
La name_box es una caja igual que la caja principal; sin embargo, esta vez, es
una caja ROW. Eso significa que el contenido se añadirá horizontalmente, e
intentará que su anchura sea lo más estrecha posible. La caja también tiene algo
de relleno —5px en todos los lados.
Ahora definimos un botón:
button = toga.Button(
"¡Hola!",
on_press=self.say_hello,
margin=5,
)
El botón también tiene 5px de margen en todos los lados. También definimos un handler —un método a invocar cuando se pulsa el botón.
A continuación, añadimos la caja de nombre y el botón a la caja principal:
main_box.add(name_box)
main_box.add(button)
Esto completa nuestro diseño; el resto del método de inicio es como antes
—definiendo una MainWindow, y asignando la caja principal como contenido de la
ventana:
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
Lo último que tenemos que hacer es definir el manejador para el botón. Un manejador puede ser cualquier método, generador o co‐rutina asíncrona; acepta el widget que generó el evento como argumento, y será invocado cada vez que se pulse el botón:
def say_hello(self, widget):
print(f"Hola, {self.name_input.value}")
El cuerpo del método es una simple sentencia print —sin embargo, interrogará el valor actual de la entrada name, y usará ese contenido como el texto que se imprime.
Ahora que hemos realizado estos cambios podemos ver que parece como inicia la aplicación otra vez. Como antes, usaremos el modo desarrollador:
(beeware-venv) $ briefcase dev
[helloworld] Iniciando en modo dev...
===========================================================================
(beeware-venv) $ briefcase dev
[helloworld] Iniciando en modo dev...
===========================================================================
(beeware-venv) C:\...>briefcase dev
[helloworld] Iniciando en modo dev...
===========================================================================
Notarás que esta vez, no instala dependencias. El portafolio Briefcase puede
detectar que la aplicación ha sido ejecutada anteriormente, y para ahorrar
tiempo, sólo ejecutará la aplicación. Si añades dependencias nuevas a tu
aplicación, puedes asegurarte que se instalan pasando una opción -r cuando
cunado ejecute briefcase dev.
Esto debería abrir una ventana IGU:
Ventana Hello World Tutorial 2, en macOS
/// subtítulo
///
Ventana Hello World Tutorial 2, en Linux
/// subtítulo
///
Ventana Hello World Tutorial 2, en Windows
/// subtítulo
///
Si introduce un nombre en el cuadro de texto y pulsa el botón IGU, debería ver aparecer la salida en la consola donde inició la aplicación.
Antes de continuar, cierra la aplicación. Al igual que en el Tutorial 1, puedes
hacerlo pulsando el botón de cierre de la ventana de la aplicación,
seleccionando Quitar/Salir en el menú de la aplicación, o escribiendo Ctrl+C
en el terminal donde ejecutaste briefcase dev.
Siguientes pasos¶
Ahora tenemos una aplicación que hace algo un poco más interesante. Pero sólo se ejecuta en nuestro propio ordenador. Vamos a empaquetar esta aplicación para su distribución. En Tutorial 3, vamos a empaquetar nuestra aplicación como un instalador independiente que podríamos enviar a un amigo, un cliente, o subir a una App Store.