Desarrollo de GUI en Python con Tkinter: Parte 3

D

Introducción

Tkinter es el paquete estándar de facto para crear GUI en Python. En la primera y segunda parte de Pharos.sh del tutorial de Tkinter, aprendimos cómo usar los componentes básicos de la GUI para crear interfaces simples.

En la última parte de nuestro tutorial, veremos un par de atajos que ofrece Tkinter para permitirnos ofrecer sin esfuerzo funciones complejas y muy útiles. También aprenderemos sobre Python Mega Widgets, un conjunto de herramientas, basado en Tkinter, que acelera la creación de interfaces complicadas aún más rápido.

Diálogo de archivo

Permitir que un usuario seleccione un archivo en su máquina es obviamente una característica muy común de las interfaces gráficas. Los cuadros de diálogo de archivos suelen ser bastante complejos: combinan al menos varios botones (como Abrir, Cancelar o Crear nueva carpeta) y un marco que muestra la estructura de los directorios de nuestro entorno. Según nuestros tutoriales anteriores, puede suponer que al usar Tkinter es muy difícil crear una función tan complicada. Sin embargo, en realidad no lo es. Eche un vistazo al siguiente ejemplo:

import tkinter
import tkinter.filedialog

root = tkinter.Tk()

def print_path():
    f = tkinter.filedialog.askopenfilename(
        parent=root, initialdir="C:/Tutorial",
        title="Choose file",
        filetypes=[('png images', '.png'),
                   ('gif images', '.gif')]
        )

    print(f)

b1 = tkinter.Button(root, text="Print path", command=print_path)
b1.pack(fill="x")

root.mainloop()

Salida:

El código anterior es todo lo que necesita para mostrar un cuadro de diálogo de archivo agradable y útil. En la línea 2 importamos el contenido de la filedialogclase. Luego, después de crear nuestra rootventana en la línea 4, definimos una nueva función en la línea 6 (que se supone que es ejecutada por el botón creado en la línea 17 y empaquetado en la línea 18).

Echemos un vistazo a la print_path()definición de la función. En la línea 7 ejecutamos la askopenfilenamefunción, que toma un par de argumentos. El primer argumento, por supuesto, es el widget padre del diálogo (que en este caso es nuestra ventana raíz). Luego, en el initialdirargumento, proporcionamos una ubicación que se mostrará en nuestro diálogo de archivo justo después de que se abra. titlecontrola el contenido de la barra de título del diálogo.

Y luego tenemos el filetypesargumento, gracias al cual podemos especificar qué tipo de archivos serán visibles para el usuario en el diálogo de archivo. Reducir los tipos de archivos puede hacer que la búsqueda del archivo deseado sea mucho más rápida, además de permitir que el usuario sepa qué tipos de archivos se aceptan.

El argumento de filetypeses una lista de tuplas de 2 elementos. En cada tupla, el primer elemento es una cadena que es cualquier descripción que queramos establecer para cada uno de los tipos de archivo. El segundo elemento es donde indicamos o enumeramos las extensiones de archivo asociadas con cada tipo de archivo (si solo hay una extensión, es una cadena; de lo contrario, es una tupla). Como puede ver en la captura de pantalla de salida anterior, el usuario puede seleccionar el tipo de archivo mostrado en la lista desplegable en la esquina inferior derecha del cuadro de diálogo.

El askopenfilename()método devuelve una cadena que es la ruta del archivo seleccionado por el usuario. Si el usuario decide presionar Cancelar, se devuelve una cadena vacía. En la línea 7 devolvemos la ruta a la variable f, y luego, en la línea 15 (que solo se ejecuta después de que se cierra el cuadro de diálogo Archivo), la ruta se imprime en la consola.

Visualización de imágenes con Tkinter

Una cosa más interesante que muchas personas pueden encontrar útil para aplicar a sus GUI es mostrar imágenes. Modifiquemos un poco el ejemplo anterior.

import tkinter
import tkinter.filedialog

root = tkinter.Tk()

def display_image():
    f = tkinter.filedialog.askopenfilename(
        parent=root, initialdir="C:/Tutorial",
        title="Choose file",
        filetypes=[('png images', '.png'),
                   ('gif images', '.gif')]
        )

    new_window = tkinter.Toplevel(root)

    image = tkinter.PhotoImage(file=f)
    l1 = tkinter.Label(new_window, image=image)
    l1.image = image
    l1.pack()

b1 = tkinter.Button(root, text="Display image", command=display_image)
b1.pack(fill="x")

root.mainloop()

Salida:

Veamos qué cambió dentro de la función ejecutada por nuestro botón, ahora renombrado display_image. Mostramos el cuadro de diálogo Archivo, usamos los mismos criterios para la selección de archivos que antes, y nuevamente almacenamos la ruta devuelta en variable f. Sin embargo, después de obtener la ruta del archivo, no lo imprimimos en la consola. Lo que hacemos es crear una ventana de nivel superior en la línea 14. Luego, en la línea 16, instanciamos un objeto de la PhotoImageclase, haciendo que lea el .pngarchivo que fue seleccionado por el usuario. Luego, el objeto se almacena en la imagevariable, que podemos pasar como argumento para la construcción del Labelwidget en la línea 17. En la línea 18, nos aseguramos de mantener una referencia al imageobjeto para evitar que se borre con la basura de Python. coleccionista. Luego, en la línea 19, empaquetamos nuestra etiqueta (esta vez mostrando una imagen, no un texto) dentro del new_window.

Selector de color

Otra característica común, especialmente en el software centrado en gráficos, es permitir al usuario seleccionar un color de una paleta. En este caso, Tkinter también ofrece una solución agradable y lista para usar que debería satisfacer la mayoría de nuestras necesidades con respecto a la función de elección de color.

import tkinter
import tkinter.colorchooser

root = tkinter.Tk()

def color_button():
    color = tkinter.colorchooser.askcolor(parent=root)
    print(color)
    b1.configure(bg=color[1])

b1 = tkinter.Button(root, text="Select Color", command=color_button)
b1.pack(fill="x")

root.mainloop()

Salida:

En la línea 2 del ejemplo que se muestra arriba, importamos una clase llamada colorchooser. Usamos su askcolor()método en la línea 7. Este método, de manera similar a askopenfilename(), es responsable de abrir un cuadro de diálogo agradable y complejo (un selector de color en este caso) y devuelve los datos dependiendo de la elección del usuario. En este caso, después de que el usuario elige un color de la paleta y acepta su elección, el objeto devuelto a la variable colores una tupla que contiene dos elementos. El primer elemento es una tupla que almacena valores para los canales rojo, verde y azul del color seleccionado. El segundo elemento de la tupla es del mismo color especificado en formato hexadecimal . Podemos ver el contenido de las tuplas en nuestra consola, gracias a la print()línea 8.

Después de almacenar la tupla devuelta por askcoloren variable color, usamos esa variable en la línea 9 para configurar el b1botón. Como ya sabes, el bgargumento se encarga de controlar el color de fondo del botón. Le pasamos el primer elemento de la colortupla (la representación del color en formato hexadecimal). Como resultado, después de presionar el b1botón, el usuario puede cambiar su color de fondo usando un bonito selector de color.

Cajas de mensajes

Antes de pasar de Tkinter a Python Mega Widgets, es bueno mencionar una característica más del módulo Tkinter que hace que la programación de GUI sea un poco más rápida. Tkinter ofrece los llamados cuadros de mensaje, que son un conjunto de cuadros de diálogo estándar sencillos pero muy utilizados . Estos cuadros de mensaje se pueden usar para mostrar un mensaje rápido, una advertencia o cuando necesitemos que nuestro usuario tome una decisión simple de sí o no. El siguiente ejemplo muestra todos los cuadros de mensajes que ofrece Tkinter:

import tkinter
import tkinter.messagebox

root = tkinter.Tk()

def display_and_print():
    tkinter.messagebox.showinfo("Info","Just so you know")
    tkinter.messagebox.showwarning("Warning","Better be careful")
    tkinter.messagebox.showerror("Error","Something went wrong")

    okcancel = tkinter.messagebox.askokcancel("What do you think?","Should we go ahead?")
    print(okcancel)

    yesno = tkinter.messagebox.askyesno("What do you think?","Please decide")
    print(yesno)

    retrycancel = tkinter.messagebox.askretrycancel("What do you think?","Should we try again?")
    print(retrycancel)

    answer = tkinter.messagebox.askquestion("What do you think?","What's your answer?")
    print(answer)

b1 = tkinter.Button(root, text="Display dialogs", command=display_and_print)
b1.pack(fill="x")

top.mainloop()

Salida:

Esta vez, nuestro b1botón ejecuta la función display_and_print(). La función permite que aparezcan 7 cuadros de mensajes en secuencia; cada uno se muestra después de que el usuario interactúa con el anterior. Los cuadros de diálogo definidos en las líneas 11 a 21 son cuadros de diálogo que requieren que el usuario elija una de las dos opciones disponibles; por lo tanto, devuelven valores basados ​​en las decisiones y los almacenan en sus respectivas variables. En cada caso, podemos pasar dos argumentos al definir los diálogos: el primero es siempre el título del diálogo y el segundo contiene el contenido de su mensaje principal.

Entonces, para comenzar desde arriba. En la línea 7, definimos un showinfocuadro de diálogo simple , que solo debe mostrar un icono neutral, un mensaje y un botón Aceptar que lo cierra. En las líneas 8 y 9, tenemos tipos simples de cuadros de mensajes similares, pero sus iconos indican que se requiere precaución por parte del usuario ( showwarning) o que se ha producido un error ( showerror). Tenga en cuenta que en cada uno de los tres casos, se reproduce un sonido diferente al aparecer el diálogo.

Como dije antes, las líneas 11 a 21 contienen código responsable de mostrar cuadros de diálogo para obtener la decisión del usuario. askokcancel(línea 11) regresa Truesi el usuario hace clic en Aceptar y Falsesi hace clic en Cancelar. askyesno(línea 14) vuelve Truesi el usuario hace clic en Sí y Falsesi el usuario hace clic en No. askretrycancel(línea 17) vuelve Truesi el usuario hace clic en Reintentar y Falsesi el usuario hace clic en Cancelar. askquestiones muy similar a askyesno, pero regresa 'yes'si el usuario hace clic en Sí y 'no'si el usuario hace clic en No.

Tenga en cuenta que la apariencia exacta del diálogo de archivo, el selector de color y todos los cuadros de mensaje depende del sistema operativo en el que se ejecuta el código, así como del idioma del sistema.

Barra de progreso

Otro elemento útil de las GUI avanzadas es una barra de progreso . El siguiente ejemplo muestra una implementación simple de esta función usando Tkinter:

import tkinter
import time
from tkinter import ttk

root = tkinter.Tk()

def start():
    for k in range(1, 11):
        progress_var.set(k)
        print("STEP", k)
        k += 1
        time.sleep(1)
        root.update_idletasks()

b1 = tkinter.Button(root, text="START", command=start)
b1.pack(side="left")

progress_var = tkinter.IntVar()

pb = ttk.Progressbar(root, orient="horizontal",
                     length=200, maximum=10,
                     mode="determinate",
                     var=progress_var)
pb.pack(side="left")

pb["value"] = 0

root.mainloop()

Salida:

El ejemplo anterior muestra la implementación de Progressbar. Es parte del módulo tkinter.ttk , que proporciona acceso al conjunto de widgets temáticos de Tk, introducido en Tk 8.5 . Es por eso que necesitamos importar adicionalmente el ttkmódulo en la línea 3.

El estado de nuestra barra de progreso se controlará por tiempo: la barra progresará en diez pasos, ejecutados en intervalos de un segundo. Para ello importamos el timemódulo en la línea 2.

Definimos nuestro Progressbaren la línea 20. Definimos su widget padre ( root), le damos una orientación “horizontal” y una lengthde 200 píxeles. Luego, definimos el maximumvalor, que es el valor de la variable asignada a la barra de progreso usando el varargumento (en nuestro caso, la progress_varvariable), eso significa que la barra de progreso está completamente llena. Establecemos el modeen “determinado”, lo que significa que nuestro código moverá la longitud del indicador a puntos definidos con precisión en función del progress_varvalor de.

La progress_varvariable entera que controlará el progreso de la barra se define en la línea 18. En la línea 26, usando una asignación similar a un diccionario, establecemos el valor inicial de la barra de progreso en 0.

En la línea 15, creamos un Buttonreloj que se supone debe iniciar el reloj controlando el progreso de nuestra barra ejecutando la start()función, definida entre las líneas 7 y 13. Allí, tenemos un forciclo simple , que iterará a través de valores entre 1 y 10. Con cada iteración , el progress_varvalor se actualiza y aumenta en 1. Para poder observar claramente el progreso, esperamos un segundo durante cada iteración (línea 12). Luego usamos el update_idletasks()método de la ventana raíz en la línea 13, para permitir que el programa actualice la apariencia de la barra de progreso aunque todavía estemos ejecutando el forciclo (entonces, técnicamente todavía estamos en una sola mainloop()iteración).

Mega widgets de Python

Si usa Tkinter ampliamente en sus proyectos, creo que es una buena idea considerar incorporar Python Mega Widgets en su código. Python Mega Widgets es un conjunto de herramientas basado en Tkinter que ofrece un conjunto de megawidgets: widgets complejos, funcionales y relativamente agradables estéticamente hechos de widgets Tkinter más simples. Lo bueno de este paquete, que puede descargar aquí, es que la filosofía general de definir y orientar widgets es la misma que en el caso de Tkinter, y puede mezclar ambas bibliotecas en su código. Terminemos nuestro tutorial rascando la superficie de este poderoso kit de herramientas.

Widget EntryField

Uno de los widgets más útiles del paquete Pmw es EntryField. Analicemos el siguiente ejemplo para ver de lo que es capaz:

import tkinter
import Pmw

root = tkinter.Tk()

def color_entry_label():
    color = entry_color.get()
    entry_number.configure(label_bg=color)

entry_color = Pmw.EntryField(root, labelpos="w",
                                 label_text="First name:",
                                 entry_bg="white",
                                 entry_width=15,
                                 validate="alphabetic")

entry_number = Pmw.EntryField(root, labelpos="w",
                                 label_text="Integer:",
                                 entry_bg="white",
                                 entry_width=15,
                                 validate="integer")

ok_button = tkinter.Button(root, text="OK", command=color_entry_label)

entry_color.pack(anchor="e")
entry_number.pack(anchor="e")
ok_button.pack(fill="x")

root.mainloop()

Salida:

Esta vez, no solo tenemos que importar tkinter, sino también nuestro Pmwpaquete recién instalado (línea 2). Como siempre, usamos la Tkclase para iniciar nuestra ventana raíz.

En las líneas 10-14 y 16-20 definimos dos Pmw.EntryFieldwidgets. An EntryFieldes una combinación funcional de Tkinter Labely Entry, con algunas funcionalidades útiles. El primer argumento para la inicialización del widget es, por supuesto, el widget padre. El label_text, entry_bgy el entry_widthcontrol de algunos aspectos explican por sí mismos de la apariencia del widget. El argumento más interesante de nuestro ejemplo es probablemente el validateargumento. Aquí, podemos decidir qué tipo de datos puede poner el usuario dentro del campo.

En el entry_colorcampo, esperamos una cadena de letras, por lo que lo configuramos validatecomo “alfabético”. En el entry_numberwidget, esperamos un número entero, y eso es lo que establecemos en el validatevalor del argumento. De esta forma, si intentamos poner un número dentro del primero y una letra dentro del segundo, los símbolos simplemente no aparecerán en los widgets y se reproducirá un sonido del sistema, informándonos que estamos intentando hacer algo mal. Además, si el widget espera cierto tipo de datos y su contenido está en conflicto con esta condición en el momento en que se inicializa, EntryFieldse resaltará en rojo.

Como puede ver en nuestro ejemplo, justo después de mostrar nuestra ventana, el primer campo de entrada es blanco y el segundo es rojo. Esto se debe a que una cadena vacía (contenido predeterminado de las entradas) entra en la categoría de entidades “alfabéticas”, pero definitivamente no es un número entero.

El botón definido en la línea 26 ejecuta el color_entry_label()comando definido entre las líneas 6 y 8. El objetivo de la función es pintar el entry_numberfondo de la etiqueta del widget de acuerdo con el contenido del entry_colorwidget. En la línea 7, el get()método se utiliza para extraer el contenido del entry_color EntryField. Entonces, naturalmente, el configure()método se utiliza para cambiar la apariencia del entry_numberwidget. Tenga en cuenta que para cambiar las características de los widgets Pmw que se componen de varios widgets más simples, tenemos que especificar qué sub-widget queremos configurar (en nuestro caso, es la etiqueta, por eso configuramos el label_bgy no, digamos , el entryfield_bg).

El EntryFieldwidget puede no ser visualmente muy impresionante, pero incluso este simple ejemplo ilustra el potencial de los mega-widgets: construir este tipo de pieza de autoverificación de la interfaz de mayor complejidad requeriría mucho más código si intentáramos lograr el mismo efecto usando Tkinter llano. Le animo a explorar otros poderosos mega-widgets descritos en la documentación del kit de herramientas .

Conclusión

Tkinter es una de las muchas bibliotecas GUI disponibles para Python, pero su gran ventaja es que se considera un estándar de Python y aún se distribuye, de forma predeterminada, con todas las distribuciones de Python. Espero que hayas disfrutado de este pequeño tutorial y que ahora conozcas bien la creación de interfaces para usuarios que podrían asustarse con el software operado por línea de comandos.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad