Desarrollo de GUI en Python con Tkinter: Parte 3

    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 鈥嬧媏n 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.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *