Introducción a Tcl/Tk (tkinter)

Introducción a Tcl/Tk (tkinter)

Tk es una herramienta para desarrollar aplicaciones de escritorio multiplataforma, esto es, aplicaciones nativas con una interfaz gráfica para sistemas operativos Windows, Linux, Mac y otros. Técnicamente, Tk es una biblioteca de código abierto escrita en C y desarrollada en sus orígenes para el lenguaje de programación Tcl; de ahí que usualmente nos refiramos a ella como Tcl/Tk. Desde sus primeras versiones Python incluye en su biblioteca o librería estándar el módulo tkinter, que permite interactuar con Tk para desarrollar aplicaciones de escritorio en Python. La curva de aprendizaje de Tk es relativamente pequeña si la comparamos con otras bibliotecas del rubro (como Qt), de modo que cualquier programador con una mínima base de Python puede comenzar rápidamente a crear aplicaciones gráficas profesionales y luego distribuirlas vía herramientas como cx_Freeze o PyInstaller, que se integran muy bien con Tk.

Aquí en Recursos Python tenemos una importante colección de artículos y códigos de fuentes relativos a Tk categorizados bajo la etiqueta tkinter, que cubren una parte importante de la biblioteca. Pero puesto que todos esos artículos suponen un mínimo conocimiento del funcionamiento y de algunos términos técnicos de Tk y del desarrollo de aplicaciones de escritorio, decidimos escribir este artículo como punto de partida y de referencia de todo el resto. Con este propósito, a continuación crearemos una pequeña aplicación que permite convertir un valor de temperatura expresado en grados Celsius a valores en Kelvin y grados Fahrenheit, como lo ilustra la siguiente imagen.

Conversor de temperatura con tkinter

Para comenzar, el primer paso para cualquier aplicación de Tk es importar los módulos correspondientes:

import tkinter as tk
from tkinter import ttk

Aquí importamos el módulo principal tkinter abreviado como tk, una convención habitual entre los programadores de Python. En segundo lugar importamos el módulo ttk que se encuentra dentro de tkinter. Estaremos utilizando objetos que están dentro de ambos módulos. Para las diferencias entre tk y ttk, véase este artículo.

El segundo paso fundamental es crear la ventana principal. Todas las aplicaciones de Tk tendrán una ventana principal, y eventualmente algunas otras ventanas secundarias.

ventana = tk.Tk()
ventana.mainloop()

En la primera línea creamos una instancia de Tk, que se ocupa de crear la ventana principal y de iniciar internamente un intérprete de Tcl y Tk, cosa que explica el nombre de la clase. En efecto, sería más claro si el nombre fuese tk.MainWindow o similar. Con propósitos didácticos hemos llamado ventana a la instancia en cuestión, pero la convención que habitúan seguir los programadores de Tk es nombrarla root (raíz, pues es el objeto del cual nacerá el resto de la interfaz).

La segunda línea ejecuta el método mainloop(), que es el bucle principal del programa. Todas las aplicaciones de escritorio (en Tk o en cualquier otra herramienta afín) trabajan con un bucle principal que se ocupa de gestionar los eventos de la interfaz gráfica. El bucle principal se está ejecutando constantemente (pues, justamente, es un bucle) y una de sus tareas principales es «dibujar» la ventana en la pantalla, por lo cual el método mainloop() solo finaliza cuando se cierra la última ventana de nuestra aplicación. Este es un dato importante a tener en cuanta siempre que atinemos a ubicar código debajo de la llamada al mainloop(). Por lo general, ventana.mainloop() será la última línea de nuestro código.

Ahora vamos a dar un título y un tamaño a la ventana, vía los métodos title() y config().

ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)
ventana.mainloop()

En cualquier método, función o clase de Tk donde se requiera especificar un tamaño, será vía los argumentos width (ancho) y height (alto), indicados en píxeles. Así, nuestra ventana tendrá al iniciarse un ancho de 400 píxeles y un alto de 300 píxeles.

Ventana del conversor de temperatura

Ahora bien, tenemos la ventana configurada y debemos empezar a llenarla de botones, cajas de texto, etiquetas, menús, casillas de verificación, etc. A cada uno de estos componentes que podemos incluir en nuestra interfaz y con los cuales el usuario puede interactuar se lo conoce como control o widget. Puedes ver una lista completa de los controles que ofrece Tk en este artículo. En términos generales podemos decir que hay dos formas de organizar el código relativo a la creación de los controles: con orientación a objetos o sin orietanción a objetos. Utilizar orientación a objetos suele ser útil para las aplicaciones más grandes y con interfaces de usuario complejas. Para aplicaciones pequeñas y medianas, prescindir de la orietanción a objetos es pertinente. En lo que sigue del artículo cada bloque de código lo presentaremos en sus dos versiones.

Empecemos por mostrar un mensaje en la ventana que indique al usuario que debe ingresar la temperatura en grados Celsius, para lo cual podemos usar un control o widget llamado etiqueta. La etiqueta está representada en Tk por la clase ttk.Label.

import tkinter as tk
from tkinter import ttk


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)

etiqueta_temp_celsius = ttk.Label(text="Temperatura en ºC:")
etiqueta_temp_celsius.place(x=20, y=20)

ventana.mainloop()

import tkinter as tk
from tkinter import ttk


class Aplicacion(ttk.Frame):

    def __init__(self, parent):
        super().__init__(parent)

        self.etiqueta_temp_celsius = ttk.Label(
            parent, text="Temperatura en ºC:")
        self.etiqueta_temp_celsius.place(x=20, y=20)


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)
app = Aplicacion(ventana)
ventana.mainloop()

Para introducir un control en la interfaz, primero debemos crear una instancia de la clase correspondiente (ttk.Label, en este caso), asignársela a una variable (etiqueta_temp_celsius) y ubicarla en algún lugar de la ventana vía el método place(). Este método requiere que indiquemos de forma absoluta la posición del control en la ventana, esto es, especificando la posición en las coordenadas X e Y. Opcionalmente podemos pasar los argumentos width y height para asignarle al control un ancho y un alto fijos; de lo contrario, Tk provee un tamaño por defecto. Para una explicación pormenorizada sobre place() y los otros métodos para posicionar controles en Tk, véase este artículo.

Debajo de la creación de la etiqueta, agreguemos la caja de texto para introducir la temperatura y el botón para realizar la conversión. Para ello usaremos dos clases nuevas: ttk.Entry y ttk.Button.

import tkinter as tk
from tkinter import ttk


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)

etiqueta_temp_celsius = ttk.Label(text="Temperatura en ºC:")
etiqueta_temp_celsius.place(x=20, y=20)

caja_temp_celsius = ttk.Entry()
caja_temp_celsius.place(x=140, y=20, width=60)

boton_convertir = ttk.Button(text="Convertir")
boton_convertir.place(x=20, y=60)

ventana.mainloop()

import tkinter as tk
from tkinter import ttk


class Aplicacion(ttk.Frame):

    def __init__(self, parent):
        super().__init__(parent)

        self.etiqueta_temp_celsius = ttk.Label(
            parent, text="Temperatura en ºC:")
        self.etiqueta_temp_celsius.place(x=20, y=20)

        self.caja_temp_celsius = ttk.Entry(parent)
        self.caja_temp_celsius.place(x=140, y=20, width=60)

        self.boton_convertir = ttk.Button(
            parent, text="Convertir")
        self.boton_convertir.place(x=20, y=60)


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)
app = Aplicacion(ventana)
ventana.mainloop()

Como se observa, la lógica es siempre la misma: primero la creación del control, luego su posicionamiento. La clase Entry() no lleva argumento text puesto que se espera que el usuario ingrese allí un dato.

Respecto del diseño de la interfaz nos resta únicamente agregar las dos etiquetas en las cuales se mostrarán los resultados de la conversión, o sea, lo valores en Kelvin y grados Fahrenheit. Agreguémoslas:

import tkinter as tk
from tkinter import ttk


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)

etiqueta_temp_celsius = ttk.Label(text="Temperatura en ºC:")
etiqueta_temp_celsius.place(x=20, y=20)

caja_temp_celsius = ttk.Entry()
caja_temp_celsius.place(x=140, y=20, width=60)

boton_convertir = ttk.Button(text="Convertir")
boton_convertir.place(x=20, y=60)

etiqueta_temp_kelvin = ttk.Label(text="Temperatura en K: n/a")
etiqueta_temp_kelvin.place(x=20, y=120)

etiqueta_temp_fahrenheit = ttk.Label(text="Temperatura en ºF: n/a")
etiqueta_temp_fahrenheit.place(x=20, y=160)

ventana.mainloop()

import tkinter as tk
from tkinter import ttk


class Aplicacion(ttk.Frame):

    def __init__(self, parent):
        super().__init__(parent)

        self.etiqueta_temp_celsius = ttk.Label(
            parent, text="Temperatura en ºC:")
        self.etiqueta_temp_celsius.place(x=20, y=20)

        self.caja_temp_celsius = ttk.Entry(parent)
        self.caja_temp_celsius.place(x=140, y=20, width=60)

        self.boton_convertir = ttk.Button(
            parent, text="Convertir")
        self.boton_convertir.place(x=20, y=60)

        self.etiqueta_temp_kelvin = ttk.Label(
            parent, text="Temperatura en K: n/a")
        self.etiqueta_temp_kelvin.place(x=20, y=120)

        self.etiqueta_temp_fahrenheit = ttk.Label(
            parent, text="Temperatura en ºF: n/a")
        self.etiqueta_temp_fahrenheit.place(x=20, y=160)


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)
app = Aplicacion(ventana)
ventana.mainloop()

Hasta aquí el diseño de la ventana. Ahora debemos hacer que efectivamente al presionar el botón nuestro programa convierta la temperatura ingresada en la caja de texto. Para ello debemos crear una función y asociarla al boton_convertir vía el argumento command. Luego, desde el interior de la función obtenemos el contenido de la caja de texto vía el método get() y arrojamos el resultado de la conversión en las etiqueta_temp_kelvin y etiqueta_temp_fahrenheit vía el método config().

import tkinter as tk
from tkinter import ttk


def convertir_temp():
    temp_celsius = float(caja_temp_celsius.get())
    temp_kelvin = temp_celsius + 273.15
    temp_fahrenheit = temp_celsius*1.8 + 32
    etiqueta_temp_kelvin.config(text=f"Temperatura en K: {temp_kelvin}")
    etiqueta_temp_fahrenheit.config(
        text=f"Temperatura en ºF: {temp_fahrenheit}")


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)

etiqueta_temp_celsius = ttk.Label(text="Temperatura en ºC:")
etiqueta_temp_celsius.place(x=20, y=20)

caja_temp_celsius = ttk.Entry()
caja_temp_celsius.place(x=140, y=20, width=60)

boton_convertir = ttk.Button(text="Convertir", command=convertir_temp)
boton_convertir.place(x=20, y=60)

etiqueta_temp_kelvin = ttk.Label(text="Temperatura en K: n/a")
etiqueta_temp_kelvin.place(x=20, y=120)

etiqueta_temp_fahrenheit = ttk.Label(text="Temperatura en ºF: n/a")
etiqueta_temp_fahrenheit.place(x=20, y=160)

ventana.mainloop()

import tkinter as tk
from tkinter import ttk


class Aplicacion(ttk.Frame):

    def __init__(self, parent):
        super().__init__(parent)

        self.etiqueta_temp_celsius = ttk.Label(
            parent, text="Temperatura en ºC:")
        self.etiqueta_temp_celsius.place(x=20, y=20)

        self.caja_temp_celsius = ttk.Entry(parent)
        self.caja_temp_celsius.place(x=140, y=20, width=60)

        self.boton_convertir = ttk.Button(
            parent, text="Convertir", command=self.convertir_temp)
        self.boton_convertir.place(x=20, y=60)

        self.etiqueta_temp_kelvin = ttk.Label(
            parent, text="Temperatura en K: n/a")
        self.etiqueta_temp_kelvin.place(x=20, y=120)

        self.etiqueta_temp_fahrenheit = ttk.Label(
            parent, text="Temperatura en ºF: n/a")
        self.etiqueta_temp_fahrenheit.place(x=20, y=160)

    def convertir_temp(self):
        temp_celsius = float(self.caja_temp_celsius.get())
        temp_kelvin = temp_celsius + 273.15
        temp_fahrenheit = temp_celsius*1.8 + 32
        self.etiqueta_temp_kelvin.config(
            text=f"Temperatura en K: {temp_kelvin}")
        self.etiqueta_temp_fahrenheit.config(
            text=f"Temperatura en ºF: {temp_fahrenheit}")


ventana = tk.Tk()
ventana.title("Conversor de temperatura")
ventana.config(width=400, height=300)
app = Aplicacion(ventana)
ventana.mainloop()

¡Excelente! La función convertir_temp() será invocada por Tk cada vez que el usuario presione el boton_convertir. Para más detalles sobre el funcionamiento de los botones, véase este artículo. El contenido de la caja de texto hemos tenido que convertirlo a un número de coma flotante vía la función incorporada float(), ya que por defecto el resultado de get() es siempre una cadena. Sobre las cajas de texto tenemos este otro artículo.

He aquí entonces una pequeña aplicación de escritorio escrita con Python y Tk y sus conceptos principales: ventana, controles, bucle principal, etc. Puedes ahora dirigirte a alguno de nuestros artículos sobre los diversos componentes de Tk en este enlace.



Deja una respuesta