Tutorial 2 - Hacerlo interesante¶
En Tutorial 1, generamos un proyecto stub 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.
El archivo __main__.py marca el módulo helloworld como un tipo especial de
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
mainde la aplicaciónhelloworld. - Es decir, importa el método
mainde la aplicaciónhelloworld; y si se está ejecutando como punto de entrada, llama al método main(), e inicia el bucle principal de la aplicación. El bucle principal es la forma en que una aplicación GUI escucha la entrada del usuario (como clics de ratón y pulsaciones de 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 esta línea por línea:
import toga
from toga.style.pack import COLUMN, ROW
En primer lugar, 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 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 widgets reales. Luego aplicas estilos a estas cajas para definir cómo consumirán el espacio disponible en la ventana.
En esta aplicación, definimos una sola caja, pero no ponemos nada en ella.
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 toga.MainWindow, que 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 la Ventana Principal se cierra, 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 el código, es posible que la ventana principal de la aplicación no se muestre. Si esto ocurre, puedes teclear Ctrl+C en el terminal donde iniciaste la aplicación. Esto detendrá la aplicación. Entonces podrás corregir el error y reiniciar la aplicación.
A continuación añadimos nuestra caja vacía como 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 un método main(). 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 contenido propio¶
Hagamos algo más interesante con nuestra aplicación HelloWorld.
Nota
No elimine las importaciones en la parte superior del archivo, o el main() en
la parte inferior. 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(
"Your name: ",
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(
"Say Hello!",
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"Hello, {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, que 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(
"Your name: ",
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 relleno a su izquierda y derecha, y ningún relleno 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 da fácil acceso 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 caja_de_nombre 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(
"Say Hello!",
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 el cuadro de nombre y el botón al cuadro 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 del 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"Hello, {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 cómo quedan iniciando de nuevo la aplicación. Como antes, usaremos el modo desarrollador:
(beeware-venv) $ briefcase dev
[helloworld] Starting in dev mode...
===========================================================================
(beeware-venv) $ briefcase dev
[helloworld] Starting in dev mode...
===========================================================================
(beeware-venv) C:\...>briefcase dev
[helloworld] Starting in dev mode...
===========================================================================
Notarás que esta vez, no instala dependencias. Briefcase puede detectar que la
aplicación ha sido ejecutada anteriormente, y para ahorrar tiempo, sólo
ejecutará la aplicación. Si añades nuevas dependencias a tu aplicación, puedes
asegurarte de que se instalan pasando una opción -r cuando ejecutes briefcase
dev.
Esto debería abrir una ventana GUI:
Ventana Hello World Tutorial 2, en macOS
Ventana Hello World Tutorial 2, en Linux
Si introduce un nombre en el cuadro de texto y pulsa el botón GUI, 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 Quit/Exit 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.