Mostrando entradas con la etiqueta Guizero. Mostrar todas las entradas
Mostrando entradas con la etiqueta Guizero. Mostrar todas las entradas

lunes, 4 de julio de 2022

Curso Guizero 4. Cambiando el Tamaño de los widgets y su Alineación.

aplicación procesador de texto



Curso Guizero 4. Cambiando el tamaño de los Widgets y su Alineación.

Un elemento importante a considerar a la hora de planificar una GUI es el tamaño de los elementos que la componen. En este capitulo, vamos a ver diferentes métodos para cambiar tanto la altura como el ancho de los widgets.

LOS GESTORES DE VENTANAS.

Antes de ponernos a cambiar el tamaño y diseño de los widgets de nuestra aplicación, es importante que tengamos en cuenta el papel que desempeña el administrador de ventanas, que es una parte del sistema operativo con el que estemos trabajando. 

El administrador de ventanas es el que finalmente establece como se van a ver nuestros widgets, donde se colocan en la ventana y que propiedades se pueden cambiar. 

Por ejemplo, el mismo código ejecutado en diferentes sistemas operativos da lugar a resultados diferentes:

el mismo widget en diferentes sistemas operativos.

De izquierda a derecha: macOS, windows10 y Rapsbian.

Aunque las diferencias entre ambos son pequeñas, no debes pensar que tu código dará lugar a los mismos resultados en diferentes sistemas operativos.


Parámetros de tamaño.

Los parámetros height (alto) y width (ancho) nos permiten cambiar fácilmente las dimensiones de los widgets.

Para especificar el alto y ancho de un widget podemos usar estos parámetros al crear el widget. Si no lo hacemos este se creará con un tamaño predeterminado. Pero también podemos especificarlos después de haberlos creado para cambiar su tamaño.

Si ejecutas el siguiente código puedes ver un ejemplo de un widget PushButton configurado con el tamaño predeterminado y otro con 10 caracteres de alto y 20 de ancho:


from guizero import App, PushButton
app = App()
button = PushButton(app, text="Tamaño por Defecto")
button_sized = PushButton(app, text="Nuevo tamaño", width=20, height=10)
app.display()

diferentes tamaños para los widgets.


Para algunos widgets la altura y anchura se miden en caracteres, es decir la cantidad de letras y símbolos que caben en el espacio. Para otros la altura y anchura se miden en pixeles. Es importante saber que widgets utilizan que medidas, ya que los caracteres no tienen el mismo tamaño que los pixeles.

En Guizero, los widgets Box, ListBox, Picture, Slider y Waffle se miden en píxeles, al igual que un PushButton que contiene una imagen.


Filling

Una forma alternativa para especificar el alto y el ancho de un widget es configurar sus parámetros height and width con "fill" (rellenar en castellano). Esto le indica al widget que utilice todo el espacio disponible. 

Un ejemplo de uso del valor "fill" con los parámetros height and width puede ser el siguiente:

from guizero import App, PushButton
app = App()
button = PushButton(app, height=10, width=20)
button_sized = PushButton(app, text="Usando todo el espacio", width="fill", height="fill")
app.display()

(No olvides poner las comillas alrededor de fill.)

uso del valor "fill"


No todos los widgets se pueden redimensionar de esta manera. Para ver cual la soportan y cual no, así como otras notas sobre el tamaño de los diferentes widgets consulta la documentación.

CONSEJO

Si tienes problemas para decidir cual debe ser el ancho y/o alto de un widget cuando estés diseñando la aplicación, empieza dibujando todo en un papel (si, si con un lápiz y un papel) e intenta que todos los elementos tengan el tamaño y las proporciones que te gustarían. Si lo haces de esta forma, posteriormente puedes medirlos y pasar esas medidas al programa. 

Ten en cuenta que un carácter mide aproximadamente 0.212 cm y que 600 caracteres tienen una longitud aproximada de 127 cms. Si lo quieres pasar a pixeles tienes que saber que 5.08 cms son unos 600 pixeles. Aunque si lo quieres usar de forma más sencilla, es posible que necesites utilizar un convertidor de unidades como este por ejemplo

Si no te decides por el lápiz y el papel y lo estás diseñando en una aplicación, por norma general el propio programa te facilita alguna opción para obtener el ancho y alto del elemento, pero ahí depende del programa que utilices.

HACIENDO QUE TODO SE ALINEE.

Cuando los widgets se colocan en un contenedor, como en una APP o un Box, en Guizero se pueden colocar de dos formas: automática o usando cuadrículas. De forma automática se utiliza el gestor de geometría PACK y si usamos cuadrículas el gestor de geometría GRID. Aunque por defecto no se contempla también podemos utilizar de forma mixta el gestor de geometría PLACE aunque no lo veremos en este capítulo lo puedes consultar en el correspondiente enlace. Esta elección de diseño es un parámetro inicial para cada contenedor y, por lo tanto, debe especificarse cuando se cree el mismo.

Empezaremos explicando como se colocan los widgets por defecto y en un capítulo posterior veremos como usar cuadrículas (GRID).

Diseño Automático.

Si no especificamos el otro gestor de geometría, por defecto los widgets se colocan de forma automática, en el orden en que se crean, comenzando en la parte superior del contenedor y colocándose en el centro del mismo. (Usan el gestor de geometría Pack)

Los widgets también se pueden alinear a la izquierda, a la derecha o en la parte inferior del contenedor usando el parámetro align. Intenta imaginar como se verá la siguiente aplicación antes de ejecutar el código:

from guizero import App, Text

# El diseño automatico está ya predeterminado.
app = App(title="align")

# Crea una serie de widgets cada uno con una alineación diferente.
top_text = Text(app, text="En la parte de arriba", align="top")
bottom_text = Text(app, text="En la parte de abajo", align="bottom")
left_text = Text(app, text="A la izquierda. ", align="left")
left_text_1 = Text(app, text="A la izquierda de nuevo", align="left")
right_text = Text(app, text="A la derecha", align="right")

app.display()


Cuando se colocan varios widgets en el mismo contenedor y con la misma alineación, aparecen por orden, por lo que left_text aparece primero que left_text_1 en este ejemplo.


colocación automática de los widgets en la ventana


Alineado de Widgtes dentro de un Box.

El alinear widgets dentro de una caja o Box nos va a permitir mejorar mucho el diseño. Un buen ejemplo es la forma de colocar algo en una esquina de la aplicación, en vez de en la parte superior, inferior, derecha o izquierda como hemos hecho hasta ahora.

Mira el código siguiente e intenta adivinar ¿Dónde aparecerá el widget PushButton?


from guizero import App, Box, PushButton
app = App()
box = Box(app, width="fill")
button = PushButton(box, align="left")
app.display()


Se puede usar una caja o Box para mantener los diferentes elementos alineados entre si. Vamos a profundizar un poco más en el concepto intentando usar lo que hemos aprendido para recrear la bandera de un país de Oceanía llamado Tonga. Utilizamos cajas para crear y colocar la cruz y rellenar el resto de la bandera.




Prueba a eliminar los elementos Box de este script (deberás cambiar los parámetros maestros de los otros widgets) y mira lo que se crea cuando se ejecuta, en comparación con la versión con las cajas.

from guizero import App, Text, Box, PushButton, TextBox

app = App(width=300, height=200)
app.bg= "white"

# Divide la bandera en dos secciones, una superior que contendrá la cruz y un bloque rojo inferior.

top = Box(app, align="top", width="fill")
bottom = Box(app, width="fill", height="fill")

# Crea un cuadro para contener la cruz, alineado a la izquierda en la sección superior.
cross = Box(top, align="left")

# Se necesitan dos cajas para construir la cruz.
flag = Box(cross)
# Establece el fondo a blanco.
flag.bg = "white"
# Haz las partes superiores de la cruz con cuatro bloques rojos del mismo tamaño.
first = Text(flag, bg="red", align="top", height=1, width=3)
second = Text(flag, bg="red", align="left", height=1, width=3)
third = Text(flag, bg="red", align="left", height=1, width=3)
fourth = Text(flag, bg="red", align="left", height=1, width=3)
# Crea el segundo cuadro directamente debajo del primero, de modo que la pieza final
# de la cruz se añadirá a la parte inferior de la misma. Intenta ejecutar el
# programa sin esta caja para ver dónde se colocaría automáticamente.
flag_cont = Box(cross)
# Establece el fondo a blanco
flag_cont.bg = "white"
# Completa la bandera
fifth = Text(flag_cont, height=1, width=3, align="bottom", bg="red")

# Rellena el resto de la bandera de color rojo.
fill = Text(top, align="left", height="fill", width="fill", bg="red")
fill_cont = Text(bottom, height="fill", width="fill", bg="red")

app.display()

Puedes encontrar el código superior en el siguiente enlace.


Construyendo un editor de texto.


Con lo que hemos visto hasta ahora vamos a intentar un desafío. Crear un procesador de textos sencillo.  Necesitarás seguramente consultar la documentación de Guizero.

Empecemos.

Primero debemos comenzar planificando que es lo que la aplicación debe hacer y que widgets se adaptan mejor a la tarea. Echa un vistazo a un editor de textos que tengas en tu ordenador para inspirarte (El block de notas puede ser un buen ejemplo, haz clic en el menú archivo para ver algunas opciones que te brinda el editor de texto)

Nuestro editor de texto va a ser muy básico pero tendrá que tener los siguientes elementos:

- Un cuadro de texto para escribir.

- Un cuadro de texto para especificar el nombre del archivo.

- Un PushButton para guardar el archivo.

- Un PushButton para leer los archivos guardados.

Tomate un momento para diseñar el editor de texto incorporando los cuatro elementos. ¿Pondrás el botón Guardar en la parte superior o inferior del GUI? ¿Lo colocarás junto al botón de abrir? ¿Qué tamaño debe tener el cuadro donde se escribirá el texto?

Iremos añadiendo cada uno de los componentes. Intenta modificar el código sobre la marcha para que los widgets se adapten a tu diseño. Recuerda que Guizero coloca los widgets en el orden en el que se crean comenzando desde la parte superior. Eso significa que a lo mejor debes reordenar el código para adaptarlo a tu diseño.

Comienza dibujando en un papel como quieres que sea tu aplicación y luego intenta crear el diseño de la misma. No te preocupes por el código ya que lo desarrollaremos a la vez que iremos explicándolo.


La forma más fácil de manejar los controles y mantenerlos juntos es utilizar una caja (Box).  Yo simplemente la llamaré caja porque vamos a meter ahí los controles de la aplicación pero realmente puedes llamarlo como quieras mientras te quede lo que representa.

from guizero import App, TextBox, PushButton, Box

app = App(title="Procesador de Textos")

# Crea una caja para meter los controles.
# Queremos que ocupe todo el ancho de la aplicación.
caja = Box(app, align="top", width="fill")

# crea un TextBox para el nombre del archivo
nombreArchivo = TextBox(caja, text="archivo.txt", width=38, align="left")

# Crea un botón Grabar, que se usará con la función para grabar posteriormente.
btn_save = PushButton(caja, text="Grabar",  align="right")

# Crea un botón Leer, que se usará con la función leer posteriormente.
btn_read = PushButton(caja, text="Leer",  align="right")

# Crea un TextBox, el cual no está en la caja y ocupa el resto de la GUI
escritorio = TextBox(app, multiline=True, height="fill", width="fill")

app.display()

Primer diseño del GUI

Por el momento los botones de Grabar y Leer no hacen nada. Para indicarles que guarden o lean un archivo hay que añadir dos funciones:

# función para leer archivos:
def push_leer():
    with open(nombreArchivo.value, "r") as f:
        escritorio.value = f.read()

# función para escribir archivos:
def push_grabar():
    with open(nombreArchivo.value, "w") as f:
        f.write(escritorio.value)

Ambas funciones usan la función "Open" de Python. Si no estás familiarizado con ella puedes encontrar más información aquí.

Antes de seguir leyendo, intenta averiguar cuales son los parámetros nombreArchivo.value y escritorio.value e imagina que hará la función cuando se la llame.

La función push_leer() abre un archivo donde el nombre del mismo coincide con la propiedad del valor del TextBox nombreArchivo. Luego establece que la propiedad del cuadro de texto "escritorio" sea el contenido del archivo, es decir carga el contenido de texto del archivo en el cuadro de texto del escritorio.

La función push_grabar() realiza lo contrario. ¿Puedes deducir cuales son los valores de nombreArchivo.value y escritorio.value en este caso?

Finalmente necesitamos programar a los botones de Grabar y Leer para que ejecuten las funciones respectivas cuando se activen. Esto se hace añadiendo el parámetro "command" a cada uno de los widgets.

btn_save = PushButton(caja, text="Grabar", align="right", command=push_grabar)

¿Puedes escribir el código equivalente para el widget btn_read?

Cuando el procesador esté más avanzado les daremos a los usuarios más opciones de estilo para el texto. Haremos esto en el próximo capítulo así que no olvides guardar el archivo.


Siguiente Capítulo.- 5. Controlando la GUI.


martes, 21 de junio de 2022

Curso Guizero 3. Diseño y apariencia.

imagen de entrada al capitulo 3


Anteriormente. Capítulo 2: Widgets y Eventos.

Mejora el diseño de tu GUI colocando widget dentro de contenedores y cambiando su tamaño y alineación.

En este capitulo veremos un tipo especifico de widget llamado Box y aprenderemos como se puede usar para controlar el diseño de nuestra GUI.

¿Qué es un "Box"?

Un "Box" es un widget que actúa como contenedor para otros widgets. Por defecto es invisible y se puede utilizar para colocar más fácilmente otros widgets y modificar su diseño.

Se puede crear un "Box" de la misma manera que los otros widgets. El siguiente código crea un Box llamada "caja" que es parte de la App llamada "raiz":

from guizero import App, Box
raiz = App()
caja = Box(raiz)
raiz.display()

Cuando creas un "Box" puedes establecer varios parámetros iniciales.

  • master: describe donde esta contenida la caja o "Box". En el ejemplo anterior la caja esta contenida dentro de raíz.
  • align: se usa para alinear la caja dentro de widget en el que se sitúa. Veremos su uso más adelante aunque te adelanto que se usa el gestor de geometría pack por defecto en Guizero y funciona de forma automática como veremos más adelante.
  • grid: en el gestor de geometría Grid se usa para indicar donde aparecerá la caja dentro de una cuadricula o grid.
  • layout: este parámetro describe que gestor de geometría se va a utilizar 'grid' o 'auto' que corresponde con lo que en tkinter es el gestor de geometría pack.
  • visible: determina si el "Box" será visible, lo cual ocurre por defecto.
  • enabled: Este parámetro determina si la caja estará habilitada  Si no se establece entonces por defecto toma el mismo valor que tenga el parámetro del widget en el que se encuentra. En el ejemplo anterior el valor del parámetro para el elemento "raiz".
  • width y height: o ancho y alto. Determinan el tamaño de la caja. Se puede especificar el valor "fill" para que utilice todo el espacio disponible o especificar manualmente el ancho y/o el alto.
  • border: especifica el grosor del borde de la caja. El valor predeterminado es cero, es decir sin borde, pero se puede crear uno estableciendo su valor a "True" o un número mayor que 1.
Un Box o caja no es visible hasta que contiene otro widget. En el siguiente ejemplo vamos a ver como añadir un widget a un Box y que sucede cuando cambiamos algunos de estos parámetros.

Poner algo en la Caja.


Si queremos poner algo dentro de un Box, simplemente tenemos que poner el parámetro master de ese widget con el nombre que hayamos utilizado para crear el Box en cuestión. Siempre es el primer parámetro dentro de los paréntesis. En el siguiente ejemplo vamos a colocar un PushButton dentro de la caja:

from guizero import App, Box, PushButton
raiz = App()
caja = Box(raiz)
# insertamos un boton en la caja (Box)
boton = PushButton(caja)
raiz.display()


Se pueden alojar múltiples widgets en la misma caja. Intenta añadir otro PushButton o cualquier otro widget para ver los resultados.

Ahora crearemos un nuevo Box y cambiaremos algunos de los parámetros iniciales. Antes de ejecutar el siguiente código, intenta imaginar cómo se verá de distinta esta en relación con el código que usaste antes.

from guizero import App, Box, PushButton
raiz = App()
caja = Box(raiz, align="left", width="fill", border=2)
boton = PushButton(caja)
boton1 = PushButton(caja, text="Otro Botón")
raiz.display()


dos botones dentro de un Box


Como veremos, los widgets de Box son extremadamente útiles. Son útiles cuando se intentan diseños de GUI más sofisticados y se pueden usar para colocar varios widgets al mismo tiempo; Veremos esto en un paso posterior. También son útiles cuando se crean muchos widgets que deben tener el mismo aspecto, como veremos a continuación.

Desafio.

- Crea una aplicación que contenga tres cajas de diferentes tamaños.

- Cambia algunos parámetros iniciales más de tus cajas.

- Crea tres widgets más y coloca cada uno dentro de un cuadro diferente. ¿Puedes adivinar cómo se verá la aplicación antes de ejecutar tu script?

Puedes encontrar una posible solución al desafío en el siguiente enlace.


Mejorando la Apariencia 

Para comenzar a mejorar la apariencia de nuestras GUIs, veremos como cambiar propiedades de las mismas, como el fondo y el color de la fuente, la fuente en si misma y el tamaño del texto.

Vamos a utilizar este programa e intentaremos mejorar su apariencia:

from guizero import App, Text, ButtonGroup, Combo, PushButton, TextBox, Box
app = App(title="Generador nombres Heroes", width=300)

mensaje1 = Text(app, text="Escoje un adjetivo")
bgp_adjetivo = ButtonGroup(app, options=["Asombroso", "Hermoso", "Encantador", "Delicioso"], selected="Asombroso")

mensaje2 = Text(app, text="Escoje un color")
txt_color = TextBox(app)

mensaje3 = Text(app, text="Elige un animal")
cmb_animal = Combo(app, options=["Oso Hormiguero", "Tejón", "Gato", "Delfín", "Velociraptor"], selected="Oso Hormiguero", width=20)

btn_make_name = PushButton(app, text='Genera un nuevo superheroe')
lbl_output = Text(app, text="El nombre de tu heroe aparecerá aquí")

app.display()

Aunque aun no está completa vamos a intentar mejorar su apariencia.


Cambiando la apariencia de tu GUI.

Hay dos enfoques diferentes para cambiar la apariencia de una GUI. 

- Cambiar un parámetro de inicio.

- Cambiar una propiedad.


Cambio de Parámetros de inicio.

Cada vez que creamos un widget en Guizero, tenemos la posibilidad de establecer una serie de parámetros. Cada widget tiene diferentes parámetros de inicio. En la mayoría, se puede cambiar el color de fondo, el tamaño, el estilo del diseño, la posición y si el widget está activo o no. Para algunos, existen otros parámetros más específicos. Por ejemplo se puede cambiar el número de líneas que se mostrarán en un cuadro de texto y establecer el contenido del texto inicial.

Para averiguar que parámetros iniciales tiene un widget, puedes consultar la documentación. Si para programar estas utilizando un IDE, en muchos de ellos también, pero no en todos, puedes ver los parámetros de un widget escribiendo, por ejemplo app=App( y esperando que aparezca un menú de parámetros iniciales.


menu de opciones iniciales



Voy a cambiar el color de fondo de la aplicación a rojo modificando la línea tres del código


app = App(title="Generador Nombres Heroes", width=300, bg="red")

cambio del fondo a color rojo



Cambiar Propiedades.


Un método alternativo para modificar la apariencia de tus widgets es establecer propiedades después de que se haya creado el widget.

Por ejemplo, si deseas cambiar el color del texto en el mensaje 2, puede escribir

mensaje2.text_color = "blue"
Cambia el color del texto del widget mensaje2 a azul. Esta línea debe agregarse antes de la línea final del script, es decir antes de la línea app.display(), pero siempre después de que el widget haya sido creado.

Para conocer todas las propiedades que se pueden cambiar en cada tipo de widget puedes consultar la documentación.


El beneficio de usar cajas (Boxes)


Si quieres cambiar el color de fondo de cada widget en una aplicación, no tienes que repetir este código en cada uno. Si metes los widgets en un cuadro, puedes modificar las propiedades del mismo y esto se heredará a todos los widgets alojados en él. 

Pará ver esto vamos a colocar los últimos tres widgets del programa anterior dentro de un "Box" o Caja para lo cual añadiremos y modificaremos el código de la siguiente forma:

from guizero import App, Text, ButtonGroup, Combo, PushButton, TextBox, Box
app = App(title="Generador nombres Heroes", width=300, bg="red")

mensaje1 = Text(app, text="Escoje un adjetivo")
bgp_adjetivo = ButtonGroup(app, options=["Asombroso", "Hermoso", "Encantador", "Delicioso"], selected="Asombroso")

mensaje2 = Text(app, text="Escoje un color")
txt_color = TextBox(app)

mensaje3 = Text(app, text="Elige un animal")
cmb_animal = Combo(app, options=["Oso Hormiguero", "Tejón", "Gato", "Delfín", "Velociraptor"], selected="Oso Hormiguero", width=20)

#Creamos un box
caja = Box(app)

# modificamos cada widget de forma que estén alojados en la caja.
btn_make_name = PushButton(caja, text='Genera un nuevo superheroe')
lbl_output = Text(caja, text="El nombre de tu heroe aparecerá aquí")

# Ahora cambiamos las propiedades de la caja.
caja.bg = "white"
caja.text_size = 12

app.display()
Aunque hayamos insertado los dos últimos widgets en un Box, aún puedes cambiar sus propiedades individuales. Poe ejemplo es posible que quieras cambiar la fuente de texto de lbl_output.

lbl_output.font = "Times New Roman"
cambiando las propiedades de los widgets


Nota: Si quieres ver la forma de especificar diferentes colores para los widgets puedes enlazar a la siguiente página del manual. Te será muy útil. 

                    Siguiente Capitulo. 4.- Cambiando el tamaño de los Widgets y su alineación.




jueves, 9 de junio de 2022

Curso Guizero. 1.- Creando una Interfaz Gráfica de Usuario (GUI)


Imagen de entrada al capítulo

Creando una Interfaz Gráfica de Usuario (GUI)

Aunque anteriormente hemos visto ya como crear GUI´s con Tkinter y Guizero he pensado que quizá al mezclar ambos procesos el resultado haya sido confuso. Así que vamos a ver, en este y los siguientes capítulos como crear Aplicaciones Gráficas exclusivamente utilizando Guizero. 

Este curso esta basado en el curso de la Raspberry Pi Foundation "Programming with GUIs."

¿ Que necesitaremos ?

Python 3

Un IDE o editor de código. El que más te guste Mu, Thonny, Visual Studio Code etc

La librería en Python de Guizero.


Instalando Guizero.

Guizero se puede instalar en Windows, Mac y Linux. Aquí vamos a ver como instalarlo en Linux, aunque es igualmente sencillo instalarlo en cualquier otro sistema. Solamente tienes que ir a su página web y seguir las instrucciones.

Para instalarlo primeramente abrimos una sesión de terminal. Luego usamos el administrador de paquetes de Python3 y tecleamos:

sudo pip3 install guizero

Si quieres instalar características adicionales tales como poder utilizar archivos jpg, escalar imágenes o usar gifts animados entonces necesitarás instalar Guizero de forma similar al comando anterior pero usando:

sudo pip3 install guizero[images]

Si ha pasado algún tiempo y queremos actualizar el paquete, entonces usaremos la siguiente opción:

sudo pip3 install guizero --upgrade


Creando tu primer GUI.

Utiliza el editor de código que hayas elegido y crea un archivo vacío llamado por ejemplo "primer_gui.py". Después copia el siguiente código o mejor, tecléalo directamente: 

from guizero import App, Text
app = App(title="Esto es mi primera GUI")

message = Text(app, text="Esta GUI es estupenda.")
app.display()

Antes de ejecutar el código, simplemente por el contexto, imagina cual será el resultado. Es una buena práctica para ir descubriendo la lógica de la aplicación y verás como con el tiempo serás capaz de ir anticipando como se desarrollará el programa.

Bien, ejecuta el programa. Deberías ver algo como esto:

primera gui creada con Guizero

Echemos un vistazo al código línea por línea para ver que es lo que hace.

- from guizero import App, Text

Esta línea le dice a Python que importe los objetos App y Text desde la librería de Guizero.

- app = App(title="Esto es mi primera GUI")

Aquí estas creando un objeto de Guizero, App y estableces su propiedad title con el título de la ventana.

En el app, en minúsculas, es donde pondremos el resto de widgets o elementos que contendrá la aplicación. 

- message = Text(app, text="Esta GUI es estupenda.")

Estás creando un widget para mostrar etiquetas de texto (Text) llamado message. Y le estamos diciendo a Python que esa etiqueta de texto (Text) aparezca en la ventana de la aplicación (app) que definimos anteriormente. También configuramos su propiedad text para que contenga el texto "Esta GUI es estupenda".

- app.display()

Ahora que ya hemos codificado como debe ser la Interfaz del Usuario, este comando le dice al ordenador que muestre la aplicación (app) en pantalla.

Esta sencilla aplicación no hace nada más por el momento que mostrar una ventana con un texto por pantalla, pero en breve le añadiremos más funcionalidades.


Propiedades.

Si has trabajado antes con la programación orientada a objetos, ya conoces que los objetos pueden tener diferentes propiedades. Si no estas familiarizado con la programación orientada a objetos, no te preocupes, el realizar interfaces gráficas de usuario es una de las mejores formas de aprender.

Una propiedad es algo que ayuda a describir un objeto como por ejemplo la etiqueta Text. Esta etiqueta tiene propiedades como color, texto, una alineación (p.ej. izquierda, derecha, centro) y un montón de otras cosas que pueden personalizarse para crear el GUI que quieras.

Una vez que has creado un widget y le has dado un nombre (en el ejemplo anterior lo llamamos message) puedes usar el nombrewidget.property = nuevo_valor para cambiar su apariencia. Por ejemplo message.textsize = 40 cambia el tamaño del texto a 40.

Ahora, reemplaza todo tu código con el siguiente:

from guizero import App, Text
app = App(title="Una Ventana")

primer_mensaje = Text(app, text="Letras GRANDES")
segundo_mensaje = Text(app, text="Esto es verde")

primer_mensaje.text_size = 40
segundo_mensaje.bg = "green"

app.display()

Cuando ejecutes el código, verás que tienes dos Text Widgets y que el primero es más grande pero con menos colores que el segundo.


widget mostrando un texto y un el color verde

Al crear elementos o widgets en Guizero, recuerda que estos aparecen en el mismo orden en el que se crean.

Un Desafío.

- Intenta modificar el programa para añadir un tercer widget, usando el elemento Text, debajo de los otros dos 

- Para que el color del texto sea rojo en lugar de verde.

- Usa una fuente de letra diferente.

Puede hacer click en el enlace para ver una posible solución.

Un aspecto a mejorar al enfrentarnos a un nuevo programa es acostumbrarnos a leer la documentación del mismo. La documentación de Guizero describe como instalarlo y crear GUIs, además de proporcionar una referencia de cada uno de los widgets que podemos usar. Por ejemplo podemos mejorar la compresión sobre la etiqueta Text consultando su documentación.


En Resumen.

- Todos los programas de Guizero necesitan crear un objeto App al cual poder añadir los demás widgets.

- Los Widgets son elementos individuales que pueden usarse para construir un GUI, como los botones o etiquetas de texto.

- Los Widgets necesitan una app maestra a la que conectarse y aparecerán en la página en el orden en el que se crearon.

- Utilizando app.display() se mostrará el GUI.


Siguiente capítulo. Widgets y Eventos.

domingo, 27 de marzo de 2022

Tipos de Ventanas en Tkinter y Guizero.

 

ventana de un programa











Tipos de Ventanas en Tkinter y Guizero.

Ahora que ya hemos visto los  diferentes tipos de widgets y de gestores de geometría existentes vamos a centrarnos en las ventanas.

Existen diferentes ventanas. Por una parte tenemos las ventanas de aplicación y por otra las de diálogos.

Ventanas de aplicación.

Suelen ser las que inician y finalizan las aplicaciones gráficas.

Ventanas de diálogos.

Surgen de las anteriores.

El conjunto de ambas, junto con los widgets que contienen, configuran la interfaz del usuario.

En el siguiente ejemplo desarrollaremos una ventana de aplicación con un botón abrir, que cada vez que se presione abra una ventana hija de dialogo diferente y en una posición distinta de la pantalla. 

Estas ventanas que se van generando se sitúan en un plano superior con respecto a las generadas anteriormente. Además podemos interactuar indistintamente con cada una de las ventanas creadas. Podemos cambiar sus posiciones, cerrarlas etc.

Para el ejemplo, usaremos el gestor de geometría pack, aunque podríamos haber utilizado cualquiera de los anteriormente vistos. 

Tkinter.

ventana de aplicación y de dialogo


from tkinter import *
from tkinter import ttk

class Aplicacion():
    
    # Declara una variable de clase para contar ventanas
    
    ventana = 0
    
    # Declara una variable de clase para usar en el
    # calculo de la posición de la ventana
    
    posx_y = 0
    
    # Declara una ventana de aplicación
    
    def __init__(self):
        
        self.raiz=Tk()
        
        # Define la dimensión de la ventana 300x200
        # que se situara en la coordenada x=500, y=50
        
        self.raiz.geometry('300x200+500+50')
        
        self.raiz.resizable(0,0)
        self.raiz.title("Ventana de aplicación")
        
        # Define botón 'Abrir' que se utilizará para
        # abrir las ventanas de diálogo. El botón
        # está vinculado con el método 'self.abrir'
        
        boton = ttk.Button(self.raiz, text="Abrir", command=self.abrir)
        boton.pack(side=BOTTOM, padx=20, pady=20)
        self.raiz.mainloop()
        
    def abrir(self):
        ''' Construye una ventana de dialogo '''
        
        # Define una nueva ventana de diálogo '''
        
        # Las ventanas de dialogo hijas se definen con TopLevel()
        self.dialogo = Toplevel()
        
        # incrementa en 1 el contador de ventanas
        # en la variable de clase a la que se puede acceder
        # desde cualquier método de la clase con la notación
        # del punto.
        Aplicacion.ventana+=1
        
        # Recalcula la posición de la ventana


        Aplicacion.posx_y += 50
        # el \ al final de la línea era para hacer un salto y que siga leyendo el código
        tamypos ='200x100+'+str(Aplicacion.posx_y)+ \
                  '+'+ str(Aplicacion.posx_y)
        print(tamypos)
        self.dialogo.geometry(tamypos)
        self.dialogo.resizable(0,0)
        
        # obtiene el identificador de la nueva ventana
        
        ident = self.dialogo.winfo_id()
        
        # Construye el mensaje de la barra de titulo
        
        titulo = str(Aplicacion.ventana)+": "+str(ident)
        print(titulo)
        self.dialogo.title(titulo)
        
        # Define el botón 'Cerrar' que cuando sea presionado
        # cerrara (destruirá) la ventana 'self.dialogo' llamando
        # al método 'self.dialogo.destroy'
        
        boton = ttk.Button(self.dialogo, text='Cerrar', command=self.dialogo.destroy)
        boton.pack(side=BOTTOM, padx=20,pady=20)
        
        # Cuando la ejecución del programa llega a este
        # punto se utiliza el método wait.window() para
        # esperar que la ventana 'self.dialogo' sea
        # destruida.
        
        # Mientras tanto se atiende a los eventos locales
        # que se produzcan, por lo que otras partes de la
        # aplicación seguirán funcionando con normalidad.
        # Si hay código después de esta línea se ejecutará
        # cuando la ventana 'self.dialogo' sea cerrada.
        
        self.raiz.wait_window(self.dialogo)
        
def main():
    mi_app = Aplicacion()
    return 0

if __name__=='__main__':
    main()   

Puedes obtener el código de la aplicación en el siguiente enlace.


Guizero

ventana principal y de dialogo en Guizero

'''ejemplo de ventanas no modales porque mientras existen es posible interactuar libremente con ellas,
con ellas, sin ningún límite, excepto que si cerramos la ventana principal se cerrarán todas
las ventanas hijas abiertas.'''

from guizero import *

def dialogo():
    ''' Crea una nueva ventana de dialogo. Cada vez que se ejecuta se dibuja una
nueva ventana, con una nueva posición y un nuevo titulo de ventana'''
    global ventana, posx_y
    # Incrementa en 1 el numero de ventanas abiertas
    ventana +=1
    # Incrementa en 50 los pixeles de la variable q controla la posición
    posx_y +=50
    # variable de texto que contendrá el tamaño y posición de la ventana en geometry
    #tama_y_posc = "200x100"+"+"+str(posx_y)+"+"+str(posx_y)
    tama_y_posc = f"200x100+{posx_y}+{posx_y}"
    print(tama_y_posc)
        
    # En guizero se usa Window() para definir una ventana de dialogo.
    window = Window(raiz)
    # obtiene el identificador de la nueva ventana
    ident = window.tk.winfo_id()
    titulo = f"{ventana}:{ident}"
    print(ident)
    window.title=titulo
    window.hide()
    window.tk.geometry(tama_y_posc)
    window.tk.resizable(0,0)
    PushButton(window, text="Cancelar", align="bottom", command=window.destroy)
    window.show()

# Variable genérica para contar las ventanas.
ventana = 0
# Variable genérica para el calculo de la posición de la ventana
posx_y = 0

#---------------------------- Ventana Principal------------------------------------    
raiz = App(title="Ventana de Aplicación")
# La geometría del objeto raiz.tk es "(tamaño_x)x(tamaño_y)+posc_x+posc_y"
raiz.tk.geometry("300x200+500+50")
raiz.tk.resizable(0,0)

# En guizero el parámetro padding afecta a la distancia del texto del botón
# respecto al borde del mismo, hace mas grande o mas pequeño al boton
boton = PushButton(raiz, text="Abrir", align="bottom", padx=20, pady=5, command=dialogo)

# Sin embargo si lo pones en el método tk afecta a la distancia al siguiente widget.
# En este caso a la distancia a la parte inferior de la ventana
boton.tk.pack(padx=20, pady=20)
#-----------------------------------------------------------------------------------


raiz.display()

Puedes encontrar el código del ejemplo en el siguiente enlace.


Ventanas Modales y No modales.

A parte de que las ventanas pueden ser principales o de dialogo, también como ya hemos visto en el ejemplo anterior aunque no lo hayamos dicho, pueden ser modales y no modales. 

Las ventanas hijas creadas en los ejemplos anteriores son "no modales" porque mientras que existan es posible interactuar independientemente con cada una de ellas, sin limite alguno, excepto que si cerramos la ventana principal se cerrarán todas ellas.

El ejemplo típico es el de las ventanas de cualquier aplicación ofimática que te permiten trabajar con varios documentos mientras están abiertas.

En las ventanas con modales esto no pasa, ya que la ventana activa, bloquea a las demás hasta que se cierre el diálogo.

El ejemplo típico es el de algunas ventanas de dialogo en la que se establecen preferencias de las aplicaciones que tienen que ser cerradas antes de poder acceder a otras.


Tkinter. Ventana Modal.

ventana modal en tkinter


Aquí la peculiaridad es que se emplea el método grab_set() para crear la ventana modal y transiet() para convertir la ventana de dialogo en una ventana transitoria, haciendo que esta se oculte cuando la ventana de la aplicación se minimice.

'''Ejemplo de ventana modal. El siguiente ejemplo sólo es posible
mantener abierta sólo una ventana hija, aunque si la cerramos podremos abrir otra.

El método grab_set() se utiliza para crear la ventana modal y el método transiet()
se emplea para convertir la ventana de diálogo en ventana transitoria,
haciendo que se oculte cuando la ventana de aplicación sea minimizada.'''

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *
from tkinter import ttk

class Aplicacion():
    
    #variables de la clase
    ventana = 0
    posx_y = 0
    
    def __init__(self):
        self.raiz=Tk()
        self.raiz.geometry('300x200+500+50')
        self.raiz.resizable(0,0)
        self.raiz.title("Ventana de aplicación")
        boton = ttk.Button(self.raiz, text="Abrir", command=self.abrir)
        boton.pack(side=BOTTOM, padx=20, pady=20)
        self.raiz.mainloop()
        
    def abrir(self):
        '''Construye una ventana de dialogo'''
        
        self.dialogo = Toplevel() #equivale a Window en guizero
        Aplicacion.ventana+=1
        Aplicacion.posx_y+=50
        tamypos = f'200x100+{Aplicacion.posx_y}+{Aplicacion.posx_y}'
        self.dialogo.geometry(tamypos)
        self.dialogo.resizable(0,0)
        ident = self.dialogo.winfo_id()
        titulo = str(Aplicacion.ventana)+": "+str(ident)
        self.dialogo.title(titulo)
        boton=ttk.Button(self.dialogo,text='Cerrar', command=self.dialogo.destroy)
        boton.pack(side=BOTTOM, padx=20, pady=20)
        
        # Convierte la ventana ''self.dialogo' en
        # transitoria con respecto a su ventana maestra
        # 'self.raiz'.
        # Una ventana transitoria siempre se dibuja sobre
        # su maestra y se ocultará cuando la maestra sea
        # minimizada. Si el argumento 'master' es
        # omitido el valor, por defecto, será la ventana
        # madre
        
        self.dialogo.transient(master=self.raiz)
        
        # El método grab_set() asegura que no haya eventos
        # de ratón o teclado que se envien a otra ventana
        # diferente a 'self.dialogo'. Se utilizará para
        # crear una ventana de tipo modal que será
        # necesario cerrar para poder trabajar con otra
        # diferente. Con ello, también se impide que la
        # misma ventana se abra varias veces.
        
        self.dialogo.grab_set()

        # Para esperar a que la ventana self.dialogo sea cerrada.
        self.raiz.wait_window(self.dialogo)
        
def main():
    mi_app = Aplicacion()
    return 0

if __name__=='__main__':
    main()        

El código de este ejemplo se encuentra en el siguiente enlace.

Guizero. Ventana Modal.

ventana modal en guizero



Por defecto en Guizero las ventanas añadidas de dialogo que se creen son no modales, funcionan de forma independiente, así que para transformarlas en modales hay que usar componentes de Tkinter.

from guizero import *
# Las listas de un solo elemento tienen que tener una coma ,
# después del elemento.
contador=[0,]
posx_y=[0,]

def dialogo():
    # En vez de variables globales (enteros, tuplas) vamos a utilizar listas que se modifican
    # por si solas a nivel global de la aplicación.
    contador[0]=contador[0]+1
    posx_y[0]=posx_y[0]+50
    ventana=Window(raiz)
    ident = ventana.tk.winfo_id()
    ventana.title=f"{contador[0]} :{ident}"
    ventana.hide()
    ventana.tk.resizable(0,0)
    dimension = f"200x100+{posx_y[0]}+{posx_y[0]}"
    ventana.tk.geometry(dimension)
    #-----------------------------------------------------------------------
    PushButton(ventana, text="Cancelar", align="bottom", command=ventana.destroy)
        # Convierte la ventana ''self.dialogo' en
        # transitoria con respecto a su ventana maestra
        # 'self.raiz'.
        # Una ventana transitoria siempre se dibuja sobre
        # su maestra y se ocultará cuando la maestra sea
        # minimizada. Si el argumento 'master' es
        # omitido el valor, por defecto, será la ventana
        # madre
    ventana.tk.transient(master=raiz.tk)
        # El método grab_set() asegura que no haya eventos
        # de ratón o teclado que se envíen a otra ventana
        # diferente a 'self.dialogo'. Se utilizará para
        # crear una ventana de tipo modal que será
        # necesario cerrar para poder trabajar con otra
        # diferente. Con ello, también se impide que la
        # misma ventana se abra varias veces.
    ventana.tk.grab_set()
    ventana.show()

raiz=App(title="Ventana de Aplicación")
raiz.tk.geometry("300x200+500+50")
raiz.tk.resizable(0,0)

boton=PushButton(raiz, text="Abrir", padx=20, pady=5, align="bottom", command=dialogo)
boton.tk.pack(pady=20)

raiz.display()

Puedes encontrar en este enlace el código del ejemplo anterior.