Saltar a contenido

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 main desde la aplicación helloworld.
  • 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.