Quizás acaba de empezar a aprender Python y ha creado una o dos aplicaciones sencillas. ¡Felicidades! Pero, ¿ahora qué? Bueno, ¿qué tal construir una interfaz gráfica de usuario ("GUI") para interactuar con su nueva y brillante aplicación de Python?
Hay varias opciones para crear aplicaciones GUI con Python, incluidas PyQt y wxPython . Esta guía, sin embargo, le presentará Tkinter .
Tkinter , que significa "interfaz Tk", es el estándar de Python para crear GUI y se incluye con la biblioteca estándar de Python . Es un enlace al kit de herramientas Tk GUI , una biblioteca gratuita de código abierto de widgets GUI que se puede usar para construir interfaces gráficas en una variedad de lenguajes de programación.
Este tutorial recorre el diseño de una aplicación GUI básica que muestra la hora local en una zona horaria seleccionada por el usuario. Los pasos crean la aplicación progresivamente y describen algunos de los conceptos clave cuando se trabaja con Tkinter , incluido el diseño de los elementos de la GUI, la captura de la entrada del usuario y la vinculación de los elementos de la GUI a los métodos de devolución de llamada.
Para completar esta guía, necesitará tener:
pip
instalada.pytz
. La biblioteca se puede instalar usando pip
.
pip install pytz
Esta guía utilizará la terminología ventana raíz y ventana principal indistintamente.
Cree una nueva aplicación de Python llamada timezone.py
y agregue las siguientes declaraciones de import
para importar los módulos Tkinter
, datetime
y pytz
.
import tkinter as tk import datetime import pytz
Esta guía incorpora el conjunto de zonas horarias de Estados Unidos que son una pequeña fracción de las zonas horarias admitidas por pytz
. La funcionalidad de la aplicación se puede ampliar agregando nombres de zona horaria pytz
adicionales. Se puede generar una lista completa de zonas horarias disponibles con pytz
ejecutando el siguiente comando:
print(pytz.all_timezones)
El conjunto de zonas horarias de Estados Unidos disponibles a través pytz
se especifica como una lista.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"]
La instanciación de la clase Tk
crea una ventana raíz que servirá como la ventana principal de la GUI de la aplicación de zona horaria.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk()
Este paso configura la ventana raíz, particularmente su título, geometría y capacidad de cambio de tamaño.
El título de la ventana se establece mediante el método del title
.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application")
Los valores de ancho y alto de la ventana, expresados en píxeles, se pueden asignar a variables para facilitar el seguimiento del código.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175
La ventana principal se puede colocar en cualquier parte de la pantalla, por ejemplo, en el centro, en la esquina superior izquierda, etc. Una ventana centrada proporciona un "aspecto" agradable y la posición central de la ventana principal se puede determinar utilizando el ancho de pantalla de la ventana winfo_screenwidth()
y métodos winfo_screenheight()
junto con algunas matemáticas simples. Estos dos métodos devuelven el ancho y la altura de la pantalla que se utilizan para calcular las coordenadas (x,y) apropiadas para centrar la ventana principal. Al igual que con el ancho y el alto de la ventana, los valores de la posición central también se pueden asignar a las variables para que el código sea más legible.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2)
Tenga en cuenta que los valores center_x
y center_y
también se expresan en píxeles y se usa int
para garantizar que ambos valores calculados sean números enteros.
La geometría de la ventana root
, que especifica el tamaño y la posición de la ventana principal, se establece mediante el método de geometry
de la ventana raíz. Para simplificar las cosas, puede usar un literal de cadena con formato, o f-string , que interpolará las expresiones de la variable de geometría en tiempo de ejecución. Según el siguiente bloque de código, el literal f-string se pasa al método de geometry
como parámetro.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}")
La capacidad de cambio de tamaño de la ventana raíz a lo largo de sus ejes x e y se puede configurar a través del método de cambio de resizable
de la ventana raíz. El método resizable
acepta parámetros de height
y width
, en ese orden. Un valor True
o distinto de cero para cualquiera de los parámetros especifica que la ventana principal se puede cambiar de tamaño a lo largo del eje asociado. Un valor False
o 0
para cualquiera de los parámetros especifica que la ventana principal no se puede cambiar de tamaño a lo largo del eje dado. La aplicación del temporizador utilizará un valor de False
tanto para el alto como para el ancho para evitar que se cambie el tamaño de la ventana principal.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False)
Los widgets se pueden organizar en la parte superior de la ventana principal de diferentes maneras mediante los administradores de geometría . En general, los widgets se pueden organizar de 3 maneras:
Empaquetado usando el método pack()
. El administrador de geometría pack()
organiza los widgets en bloques antes de agregarlos a una ventana raíz o widget principal. Una forma imperfecta pero quizás útil de pensar en empacar es pensar en agregar artículos comestibles a una bolsa de compras. Los elementos no se agregan necesariamente a ubicaciones predefinidas en la bolsa. Más bien, se empaquetan uno tras otro, utilizando el espacio disponible hasta que todos los artículos se han colocado en la bolsa. El administrador de geometría pack()
funciona de manera similar.
Colocado usando el método place()
. El administrador de geometría place()
coloca los widgets en posiciones específicas predefinidas en la ventana raíz o el widget principal. Este administrador de geometría es obviamente útil cuando se crean diseños de GUI precisos.
Organizado como una cuadrícula usando el método grid()
. Las cuadrículas son tablas bidimensionales de filas y columnas. Los widgets se agregan a una cuadrícula especificando la fila y la columna en particular donde se debe colocar el widget. Las cuadrículas se configuran utilizando los métodos columnconfigure
y rowconfigure
de la ventana raíz. Cada uno de estos métodos tiene un index
y un atributo de weight
. El atributo index
especifica la posición de la columna o fila utilizando una base inicial de 0
. El atributo de weight
especifica el tamaño de una columna o fila en particular en relación con otras columnas. Por ejemplo, si la columna 0 tiene peso 1
y la columna 1 tiene peso 3
, entonces la columna 1 será 3 veces más grande que la columna 0 .
Las cuadrículas son útiles cuando se colocan elementos uno al lado del otro y son relativamente simples de implementar. La aplicación de zona horaria utilizará una cuadrícula de ventana principal dividida en 4 columnas iguales.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1)
En este paso, los widgets se agregan a la cuadrícula de la ventana principal definida en el Paso 4. La aplicación de zona horaria utilizará 3 widgets de TKinter :
Label
Listbox
Button
Los widgets de Label
y Button
de Tkinter son exactamente como se nombran. El widget de Label
permite al programador mostrar texto (es decir, una etiqueta), y el widget de Button
se usa para mostrar botones. El widget Listbox
se utiliza para seleccionar un valor (o valores) de una lista de opciones.
Por supuesto, hay otros widgets además de los 3 que se enumeran arriba, como los SpinBox
, Entry
y Message
. La documentación formal de los comandos de Tk es un recurso útil para obtener más información sobre los widgets disponibles a través del kit de herramientas de Tk.
Se crea una instancia de cada clase de widget para un widget determinado utilizado con el diseño de GUI. La aplicación de zona horaria utiliza 4 instancias de widgets: 2 widgets de Label
, 1 widget de Listbox
de lista y 1 widget de Button
.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="")
Como se ve en el bloque de código anterior, cada instancia de widget está "adjunta" a la ventana root
. El widget Listbox
también requiere la instanciación de una clase especial Tkinter Variable()
que se utiliza para proporcionar al widget la lista de opciones de zona horaria que el usuario puede seleccionar a través del atributo Listbox
listvariable
. El texto que muestra cada widget de Label
se puede configurar mediante el atributo de text
. El valor de texto para time_label
se deja en blanco, ya que se establecerá dinámicamente cada vez que el usuario "obtenga" la hora de una zona horaria seleccionada.
Las instancias del widget creadas en el Paso 5a se pueden colocar en la cuadrícula definida en el Paso 4 usando el método grid()
. La aplicación de zona horaria utilizará los siguientes atributos del método grid()
para definir el diseño:
column
: especifica la columna particular donde se colocará el widget utilizando una base 0
.
row
: especifica la fila particular donde se colocará el widget utilizando una base 0
.
columnspan
: especifica que el widget debe abarcar el número especificado de columnas. Por ejemplo, un widget con un valor columnspan=3
abarcará 3 columnas incluso si el widget en sí es más pequeño que 3 columnas.
sticky
: el comportamiento predeterminado de Tkinter es colocar un widget en el centro horizontal y vertical de la celda (es decir, en una posición particular de fila/columna) donde se coloca. Este comportamiento predeterminado se puede anular utilizando el atributo sticky
que utiliza valores similares a los de una brújula, incluidos NW
, N
, NE
, W
, E
, SW
, S
y SE
, para alinear el widget en una ubicación particular dentro de la celda del widget. Por ejemplo, sticky=tK.W
especifica que el widget debe alinearse con la esquina oeste de su celda de cuadrícula. De manera análoga, sticky=tK.E
especifica que el widget debe alinearse con la esquina este de su celda de cuadrícula.
padx
, pady
: los atributos padx
y pady
se usan para agregar relleno en el eje x y el eje y respectivamente con valores especificados en píxeles. Naturalmente, el relleno puede proporcionar una GUI de aspecto más profesional, lo que garantiza que los widgets no "golpeen" directamente contra los bordes de la ventana raíz u otros widgets.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10)
Se debe definir un método de devolución de llamada para manejar eventos cuando el usuario hace clic en el botón select_timezone_button
creado en el Paso 5.
Antes de definir la lógica de devolución de llamada, es útil enlazar primero el botón con el nombre del método que eventualmente abarcará el código de devolución de llamada. El método bind()
se puede usar junto con una función lambda
para vincular select_timezone_button
al método de devolución de llamada especificado. Además, tenga en cuenta que la función lambda
se usa para pasar referencias a los widgets select_timezone_listbox
y time_label
como parámetros de devolución de llamada. Estos parámetros de devolución de llamada en realidad no son necesarios ya que select_timezone_listbox
y time_label
están dentro del alcance global. Sin embargo, podría decirse que es útil demostrar cómo se pueden pasar argumentos a la función de devolución de llamada.
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_button.bind("<Button>", lambda e, args=[select_timezone_listbox, time_label]: get_timezone_time(e, args))
La lógica de devolución de llamada para manejar eventos de clic de botón se define a continuación.
def get_timezone_time(e, args): select_timezone_listbox = args[0] time_label = args[1] selection_index = select_timezone_listbox.curselection() selected_timezone = select_timezone_listbox.get(selection_index) now_time = datetime.datetime.now() tz_time = now_time.astimezone(pytz.timezone(selected_timezone)) tz_formatted = tz_time.strftime("%H:%M:%S") time_label.configure({"text": f"The time in {selected_timezone} is {tz_formatted}."}) time_label.update()
Los curselection()
y get()
se utilizan para recuperar la zona horaria seleccionada por el usuario desde la referencia al widget select_timezone_listbox
. La hora actual del usuario se convierte luego en la hora de la zona horaria seleccionada. Finalmente, el método configure
se usa para cambiar el atributo de text
de la referencia a time_label
con la hora local en la zona horaria seleccionada. Tenga en cuenta que el método update()
se usa para "forzar" al widget time_label
a actualizarse con el nuevo valor de texto.
El método mainloop()
de la ventana raíz se aplica como la última línea de código. El método mainloop()
ejecutará la GUI en un bucle infinito hasta que el usuario salga.
El código completo es el siguiente:
import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] def get_timezone_time(e, args): select_timezone_listbox = args[0] time_label = args[1] selection_index = select_timezone_listbox.curselection() selected_timezone = select_timezone_listbox.get(selection_index) now_time = datetime.datetime.now() tz_time = now_time.astimezone(pytz.timezone(selected_timezone)) tz_formatted = tz_time.strftime("%H:%M:%S") time_label.configure({"text": f"The time in {selected_timezone} is {tz_formatted}."}) time_label.update() root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10) # Bind button to callback. select_timezone_button.bind("<Button>", lambda e, args=[select_timezone_listbox, time_label]: get_timezone_time(e, args)) root.mainloop()
Cuando se inicia la aplicación, la GUI debería verse así:
Para usar la aplicación, haga clic en el cuadro de lista y use las teclas de cursor arriba/abajo de su teclado para desplazarse por las opciones de zona horaria. Haga clic en el botón Obtener hora para mostrar la hora actual en su zona horaria seleccionada.
¡Felicitaciones por crear su primera aplicación GUI de Python con Tkinter ! Como se mencionó en la Introducción, esta guía fue diseñada para presentarle algunos conceptos básicos. La documentación de la biblioteca estándar de Python y las referencias de la documentación de los comandos Tk mencionadas anteriormente son dos recursos fantásticos para ayudarlo a conocer las funciones y funciones más avanzadas de Tkinter.
Las aplicaciones GUI de Tkinter a veces son criticadas por tener una apariencia no nativa. Eso puede ser cierto. Pero los widgets del kit de herramientas son altamente configurables y las aplicaciones GUI de Tkinter se pueden construir con relativa rapidez sin necesidad de instalar ningún paquete externo de Python.
Más allá de la documentación formal, existen innumerables tutoriales disponibles a través de Internet para aprender a crear aplicaciones GUI más sofisticadas con Tkinter.