Curso de Python #09 (Nivel básico)
Imagen generada por Leonardo.Ai1. Interfaces Gráficas I
1.1. Vídeo
1.2. Notas personales
En esta lección, comenzamos el estudio de las interfaces gráficas en
Python, analizando para ello la librería Tkinter. Las interfaces gráficas,
también denominadas GUI, son intermediarios entre el programa y el usuario.
Están formadas por un conjunto de gráficos como ventanas, botones, menús,
casillas de verificación, etc.
Además de la mencionada, existen otras librerías alternativas para trabajar con interfaces gráficas:
WxPythonPyQTPyGTK
Tkinter es un ‘‘puente’’ entre Python y la librería TCL / TK.
La estructura de una interfaz gráfica en Python es:
- Raíz: la ventana de la aplicación propiamente dicha.
- Frame: estructura que agrupa diversos elementos.
- Widgets: elementos de la aplicación. En ocasiones, al frame también se le considera un widget más.
A continuación, veamos cómo construir la raíz:
from tkinter import Tk
raiz = Tk()
raiz.mainloop()
Al ejecutar el anterior bloque de código aparece una ventana en blanco en nuestro escritorio, con algunos botones que permiten interactuar con ella a la manera que estamos habituados.

Para que una ventana pueda mantenerse en ejecución, debe estar en una especie de
‘‘bucle infinito’’ (a la espera o escucha de eventos), estado que conseguimos a
través de la función mainloop(), que, por el momento, habrá de estar siempre
al final de nuestros programas.
La documentación para la librería Tkinter la podemos encontrar siguiendo
este enlace.
Modifiquemos algunas de las características que esta ventana posee por defecto:
from tkinter import Tk
raiz = Tk()
raiz.title("Ventana de pruebas")
raiz.resizable(width=False, height=False) # raiz.resizable(0, 0)
raiz.iconbitmap("icon.ico")
raiz.geometry("450x300")
raiz.config(bg="lightblue")
raiz.mainloop()
Notas:
title()nos permite cambiar el título de la ventana generada.resizable()acepta dos valores booleanos para indicar si permitimos que se modifique la anchura o la altura de la ventana. Según los argumentos escogidos, incluso queda deshabilitado el botón de maximizar ventana.iconbitmap()nos da la posibilidad de cambiar el icono de la ventana generada, que, por defecto, es una especie de pluma. Para ello, hemos de almacenar en el directorio de la aplicación (o tener bien localizada su ruta) un archivo de extensión.ico(buscar en Google ‘‘conversor .ico’’ para acceder a aplicaciones online que nos generen este tipo de ficheros).geometry()configura el ancho y el alto de la ventana.config(), entre otras acciones, nos permite cambiar el color del fondo.

Hasta el momento, las ventanas requieren de la consola de Python para su
funcionamiento. Si queremos que este sea independiente de ella, hemos de
modificar la extensión de la aplicación de .py a .pyw.
2. Interfaces Gráficas II
2.1. Vídeo
2.2. Notas personales
Después de introducir, en la lección anterior, la raíz de una interfaz gráfica, en esta abordaremos la construcción y uso de frames. Comencemos recuperando el código fuente de la ‘‘aplicación’’ que generamos con anterioridad:
from tkinter import Tk
raiz = Tk()
raiz.title("Ventana de pruebas")
raiz.resizable(width=False, height=False) # raiz.resizable(0, 0)
raiz.iconbitmap("icon.ico")
raiz.geometry("450x300")
raiz.config(bg="lightblue")
raiz.mainloop()
A continuación, crearemos un frame y lo empaquetaremos (ubicaremos) dentro de la
raíz disponible a través del método pack(). Además, prescindiremos de la
instrucción raiz.geometry(), para así estar en condiciones de configurar el
tamaño del frame (la raíz se adaptará automáticamente al tamaño de sus
elementos internos):
from tkinter import Tk, Frame
raiz = Tk()
raiz.title("Ventana de pruebas")
raiz.resizable(width=True, height=True)
raiz.iconbitmap("icon.ico")
raiz.config(bg="lightblue")
frame = Frame()
frame.pack()
frame.config(bg="tomato", width="450", height="300")
raiz.mainloop()
A primera vista, al ejecutar el aterior bloque de código, da la sensación de que
hemos perdido el color de fondo declarado para la aplicación (lightblue). No
obstante, como ahora permitimos manipular el tamaño de la ventana (mediante la
instrucción raiz.resizable(width=True, height=True)), al agrandarla
comprobamos que todo funciona correctamente.

Nota: en
esta página
podemos encontrar un buen recurso para acceder a una paleta de colores
declarados por nombres y disponibles para la librería tkinter.
Por otro lado, observamos que el frame tiene un tamaño fijo, por mucho que
manipulemos el tamaño de la ventana, las dimensiones del frame no se alteran,
así como su posición, que permanece centrada en la parte superior de la ventana
de la aplicación. Este comportamiento es el dado por defecto, que podemos
configurar de manera diferente, si así lo deseamos, a través del método
pack().
from tkinter import Tk, Frame
raiz = Tk()
raiz.title("Ventana de pruebas")
raiz.resizable(width=True, height=True)
raiz.iconbitmap("icon.ico")
raiz.config(bg="lightblue")
frame = Frame()
frame.pack(side="left", anchor="s", fill="x", expand="True")
frame.config(bg="tomato", width="450", height="300")
raiz.mainloop()

Las opciones de configuración de los frames son ciertamente numerosas. Por
ejemplo, podemos añadirle un borde (parámetros bd y relief) o cambiar el
icono del ratón cuando se adentra en el interior del frame (parámetro
cursor):
from tkinter import Tk, Frame
raiz = Tk()
raiz.title("Ventana de pruebas")
raiz.resizable(width=True, height=True)
raiz.iconbitmap("icon.ico")
raiz.config(bg="lightblue")
frame = Frame()
frame.pack(side="left", anchor="s")
frame.config(bg="tomato",
width="450",
height="300",
bd=35,
relief="groove",
cursor="pirate")
raiz.mainloop()

Obviamente, todo aquello que hemos visto de cara a configurar un frame es aplicable a la propia raíz y dependerá de cómo deseemos diseñar nuestra aplicación.
3. Interfaces Gráficas III
3.1. Vídeo
3.2. Notas personales
En esta lección, analizaremos cómo trabajar con el widget Label,
perteneciente a la librería tkinter, que nos permite mostrar texto o imágenes
en nuestras interfaces gráficas. No es un elemento con el que podamos
interactuar, es decir, no podremos borrarlo, arrastrarlo, etc.
Su sintaxis es:
variable = Label(contenedor, opciones)
En esta página podemos consultar qué
opciones disponibles ofrece el widget Label.
Para ver en acción este widget, reutilicemos como base parte del código generado en lecciones anteriores:
from tkinter import Tk, Frame
root = Tk()
root.title("Probando el widget Label")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=500, height=400)
frame.pack()
root.mainloop()
Añadimos ahora, antes de la instrucción root.mainloop(), las líneas de código:
label = Label(frame, text="Mi primera etiqueta.")
label.pack()

Quedando el resultado de la ejecución como una ventana bastante reducida porque,
recordemos, la raíz se adapta al tamaño de sus elementos integrados (aunque le
hayamos indicado ciertas dimensiones previamente). Ello se debe al uso del
método pack(). Veamos el efecto que produce utilizar la función place() en
su lugar, pasándole las coordenadas donde deseamos situar el widget:
from tkinter import Tk, Frame, Label
root = Tk()
root.title("Probando el widget Label")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=500, height=400)
frame.pack()
label = Label(frame, text="Mi primera etiqueta.")
label.place(x=100, y=200)
root.mainloop()

Nota: Python utiliza un curioso sistema de coordenadas. La coordenada x
indica la distancia horizontal al comienzo del widget tal como estamos
acostumbrados en matemáticas. Sin embargo, la coordenada y indica la distancia
vertical al comienzo del widget tomando como referencia la parte superior de
la ventana y siendo los valores positivos desplazamientos hacia abajo.
Si no vamos a utilizar en ningún momento la variable label, podemos aligerar
un tanto el código escribiendo:
from tkinter import Tk, Frame, Label
root = Tk()
root.title("Probando el widget Label")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=500, height=400)
frame.pack()
Label(frame, text="Mi primera etiqueta.").place(x=100, y=200)
root.mainloop()
A partir de aquí, ya únicamente nos queda experimentar con las diferentes
opciones asociadas al widget Label:
from tkinter import Tk, Frame, Label
root = Tk()
root.title("Probando el widget Label")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=500, height=400)
frame.pack()
Label(frame, text="Mi primera etiqueta.", fg="tomato",
font=("Arial", 18)).place(x=100, y=200)
root.mainloop()

Adicionalmente, el widget Label nos permite incluir (de forma nativa en la
librería tkinter) imágenes de tipo gif o png. Por ejemplo, insertemos en
nuestra aplicación esta imagen, que, en un
alarde de originalidad, llamaremos avengers.png:
from tkinter import Tk, Frame, Label, PhotoImage
root = Tk()
root.title("Probando el widget Label")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=500, height=400)
frame.pack()
imagen = PhotoImage(file="avengers.png")
Label(frame, image=imagen).pack() # .place() es otra posibilidad
root.mainloop()

Nota: para que la ventana se adaptase automáticamente al tamaño de la imagen
descargada, he utilizado el método pack() en lugar de la función place(). No
obstante, con ambas opciones se puede conseguir el mismo resultado si ajustamos
de manera fina sus correspondientes opciones.
4. Interfaces Gráficas IV
4.1. Vídeo
4.2. Notas personales
En esta lección, tras haber estudiado en la anterior el widget Label,
abordaremos el uso del widget Entry, cuyo funcionamento es realmente similar
a nivel de sintaxis. Este último habilita, en nuestras ventanas, la posibilidad
de introducir un cuadro de texto, desde el cual el usuario puede suministrar
cierta información.
from tkinter import Tk, Entry
root = Tk()
root.title("Probando el widget Entry")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
cuadro_texto = Entry(root)
cuadro_texto.pack()
root.mainloop()

A partir de ahora, podemos reutilizar los conceptos y propiedades aprendidas hasta el momento:
from tkinter import Tk, Frame, Entry
root = Tk()
root.title("Probando el widget Entry")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=450, height=300)
frame.pack()
cuadro_texto = Entry(frame)
cuadro_texto.place(x=100, y=100)
root.mainloop()

Empecemos a combinar widgets añadiendo al lado del cuadro de texto una
etiqueta que rece ‘‘Nombre:’’, como si quisiéramos elaborar un formulario de
registro de datos personales. Utilizando la función place() es posible, pero
resulta muy complicado cuadrar adecuadamente todos los espacios de la ventana de
la aplicación.
Existen dos alternativas a la hora de abordar esta situación:
pack(), aunque ya sabemos que después se ajustará la ventana al tamaño de sus elementos internos, ignorando pues las dimensiones que establecimos en su declaración.grid(), construye una tabla dentro de la interfaz gráfica con tantas filas y columnas como nosotros queramos. Tras ello, podemos ubicar en la casilla que deseemos el elemento que nos interese.
Estudiemos el uso de esta última función mencionada:
from tkinter import Tk, Frame, Entry, Label
root = Tk()
root.title("Probando el widget Entry")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=450, height=300)
frame.pack()
nombre_label = Label(frame, text="Nombre:")
nombre_label.grid(row=0, column=0)
cuadro_nombre = Entry(frame)
cuadro_nombre.grid(row=0, column=1)
apellido_label = Label(frame, text="Apellido:")
apellido_label.grid(row=1, column=0)
apellido_texto = Entry(frame)
apellido_texto.grid(row=1, column=1)
direccion_label = Label(frame, text="Dirección:")
direccion_label.grid(row=2, column=0)
direccion_texto = Entry(frame)
direccion_texto.grid(row=2, column=1)
root.mainloop()

Por defecto, los elementos se alinean centrados dentro de su correspondiente
casilla de la rejilla. Con el parámetro sticky y los cuatro puntos cardinales
(n, e, s, w y sus combinaciones por parejas) podemos modificar dicho
comportamiento.
from tkinter import Tk, Frame, Entry, Label
root = Tk()
root.title("Probando el widget Entry")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=450, height=300)
frame.pack()
nombre_label = Label(frame, text="Nombre:")
nombre_label.grid(row=0, column=0, sticky="e")
cuadro_nombre = Entry(frame)
cuadro_nombre.grid(row=0, column=1)
apellido_label = Label(frame, text="Apellido:")
apellido_label.grid(row=1, column=0, sticky="e")
apellido_texto = Entry(frame)
apellido_texto.grid(row=1, column=1)
direccion_label = Label(frame, text="Dirección:")
direccion_label.grid(row=2, column=0, sticky="e")
direccion_texto = Entry(frame)
direccion_texto.grid(row=2, column=1)
tfno_label = Label(frame, text="Teléfono (fijo):")
tfno_label.grid(row=3, column=0, sticky="e")
tfno_texto = Entry(frame)
tfno_texto.grid(row=3, column=1)
movil_label = Label(frame, text="Teléfono (móvil):")
movil_label.grid(row=4, column=0, sticky="e")
movil_texto = Entry(frame)
movil_texto.grid(row=4, column=1)
root.mainloop()

Para evitar que los elementos aparezcan tan juntos, los parámetros padx y
pady pueden resultarnos de utilidad, ya que nos permiten configurar la
distancia del elemento al límite del contenedor donde se haya ubicado.
from tkinter import Tk, Frame, Entry, Label
root = Tk()
root.title("Probando el widget Entry")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=450, height=300)
frame.pack()
nombre_label = Label(frame, text="Nombre:")
nombre_label.grid(row=0, column=0, sticky="e", padx=2, pady=2)
cuadro_nombre = Entry(frame)
cuadro_nombre.grid(row=0, column=1, padx=2, pady=2)
apellido_label = Label(frame, text="Apellido:")
apellido_label.grid(row=1, column=0, sticky="e", padx=2, pady=2)
apellido_texto = Entry(frame)
apellido_texto.grid(row=1, column=1, padx=2, pady=2)
direccion_label = Label(frame, text="Dirección:")
direccion_label.grid(row=2, column=0, sticky="e", padx=2, pady=2)
direccion_texto = Entry(frame)
direccion_texto.grid(row=2, column=1, padx=2, pady=2)
tfno_label = Label(frame, text="Teléfono (fijo):")
tfno_label.grid(row=3, column=0, sticky="e", padx=2, pady=2)
tfno_texto = Entry(frame)
tfno_texto.grid(row=3, column=1, padx=2, pady=2)
movil_label = Label(frame, text="Teléfono (móvil):")
movil_label.grid(row=4, column=0, sticky="e")
movil_texto = Entry(frame)
movil_texto.grid(row=4, column=1)
root.mainloop()

Ahora, ya únicamente nos resta experimentar con la configuración de los
distintos widgets estudiados, empleando para ello principalmente la función
config() asociada a cada widget.
Finalmente, veamos cómo añadir un cuadro de texto que nos permita introducir
contraseñas. Buscamos que al suministrar la información, esta aparezca
‘‘oculta’’ tras asteriscos. El parámetro show de la función config() cumple
dicho cometido:
from tkinter import Tk, Frame, Entry, Label
root = Tk()
root.title("Probando el widget Entry")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=450, height=300)
frame.pack()
nombre_label = Label(frame, text="Nombre:")
nombre_label.grid(row=0, column=0, sticky="e", padx=2, pady=2)
cuadro_nombre = Entry(frame)
cuadro_nombre.grid(row=0, column=1, padx=2, pady=2)
apellido_label = Label(frame, text="Apellido:")
apellido_label.grid(row=1, column=0, sticky="e", padx=2, pady=2)
apellido_texto = Entry(frame)
apellido_texto.grid(row=1, column=1, padx=2, pady=2)
direccion_label = Label(frame, text="Dirección:")
direccion_label.grid(row=2, column=0, sticky="e", padx=2, pady=2)
direccion_texto = Entry(frame)
direccion_texto.grid(row=2, column=1, padx=2, pady=2)
tfno_label = Label(frame, text="Teléfono (fijo):")
tfno_label.grid(row=3, column=0, sticky="e", padx=2, pady=2)
tfno_texto = Entry(frame)
tfno_texto.grid(row=3, column=1, padx=2, pady=2)
movil_label = Label(frame, text="Teléfono (móvil):")
movil_label.grid(row=4, column=0, sticky="e")
movil_texto = Entry(frame)
movil_texto.grid(row=4, column=1)
pass_label = Label(frame, text="Constraseña:")
pass_label.grid(row=5, column=0, sticky="e")
pass_texto = Entry(frame)
pass_texto.grid(row=5, column=1)
pass_texto.config(show="*")
root.mainloop()

5. Interfaces Gráficas V
5.1. Vídeo
5.2. Notas personales
En esta lección, presentaremos dos widgets nuevos: Text y Button. El
primero de ellos nos permite introducir un texto de extensión considerable en un
cuadro, mientras que el segundo simplemente se trata de la clase asociada a los
botones que habitualmente pulsamos en cualquier aplicación.
Retomemos el último ejemplo de la lección anterior:
from tkinter import Tk, Frame, Entry, Label
root = Tk()
root.title("Probando el widget Entry")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=450, height=300)
frame.pack()
nombre_label = Label(frame, text="Nombre:")
nombre_label.grid(row=0, column=0, sticky="e", padx=2, pady=2)
cuadro_nombre = Entry(frame)
cuadro_nombre.grid(row=0, column=1, padx=2, pady=2)
apellido_label = Label(frame, text="Apellido:")
apellido_label.grid(row=1, column=0, sticky="e", padx=2, pady=2)
apellido_texto = Entry(frame)
apellido_texto.grid(row=1, column=1, padx=2, pady=2)
direccion_label = Label(frame, text="Dirección:")
direccion_label.grid(row=2, column=0, sticky="e", padx=2, pady=2)
direccion_texto = Entry(frame)
direccion_texto.grid(row=2, column=1, padx=2, pady=2)
tfno_label = Label(frame, text="Teléfono (fijo):")
tfno_label.grid(row=3, column=0, sticky="e", padx=2, pady=2)
tfno_texto = Entry(frame)
tfno_texto.grid(row=3, column=1, padx=2, pady=2)
movil_label = Label(frame, text="Teléfono (móvil):")
movil_label.grid(row=4, column=0, sticky="e")
movil_texto = Entry(frame)
movil_texto.grid(row=4, column=1)
pass_label = Label(frame, text="Constraseña:")
pass_label.grid(row=5, column=0, sticky="e")
pass_texto = Entry(frame)
pass_texto.grid(row=5, column=1)
pass_texto.config(show="*")
root.mainloop()
Añadamos, a continuación del campo declarado para la introducción de la
contraseña, uno dedicado a la biografía de la persona que está rellenando el
formulario. Ello nos permitirá hacer uso de la clase Text, que habremos de
importar al inicio del código. Así, si escribimos:
bio_label = Label(frame, text="Biografía:")
bio_label.grid(row=6, column=0, sticky="e", padx=2, pady=2)
bio_texto = Text(frame, width=15, height=5)
bio_texto.grid(row=6, column=1, padx=2, pady=2)
scroll_vert = Scrollbar(frame, command=bio_texto.yview)
scroll_vert.grid(row=6, column=2, sticky="nsew")
bio_texto.config(yscrollcommand=scroll_vert.set)

Notas:
- Conviene que declaremos unas longitudes adecuadas mediante los parámetros
widthyheight, ya que las asignadas por defecto son ciertamente elevadas. - El widget
Textautomáticamente permite la posibilidad de scroll, aunque si deseamos que aparezca una barra de desplazamiento lateral, hemos de indicarlo. Para ello, se requiere la construcción de un objeto de la claseScrollbar(que hemos de importar de la libreríatkinter) y asociarlo al cuadro de texto generado. La correspondiente instrucción que se ocupa de tal tarea en el bloque de código anterior es:
scroll_vert = Scrollbar(frame, command=bio_texto.yview)
- Acto seguido, mediante el método
grid(), y teniendo cuidado con el valor correspondiente para el parámetrocolumn, terminamos haciendo que aparezca en la ventana de la aplicación. Para que se adapte al tamaño del cuadro de texto asociado, una posible estrategia es incluirsticky="nsew"en la declaración del scroll. - Por otro lado, si queremos que se posicione la barra de desplazamiento al
nivel del texto que estamos introduciendo, incluiremos la instrucción
bio_texto.config(yscrollcommand=scroll_vert.set)tras la declaración de la variablescroll_vert.
Pasemos ahora a añadir un botón a nuestra interfaz gráfica, para lo cual haremos
uso de la clase Button. Posicionemos uno en la raíz (root) de la ventana de
la aplicación:
boton_envio = Button(root, text="Enviar")
boton_envio.pack()

A continuación, veamos cómo añadir cierta funcionalidad al botón generado. Para
ello, incluimos en su declaración el parámetro command=codigo_boton,
boton_envio = Button(root, text="Enviar", command=codigo_boton)
donde codigo_boton será una función que contendrá el código con las acciones
que deseemos se lleven a cabo cuando el usuario pulse sobre el botón.
Por ejemplo, aunque no sea la característica habitual de este tipo de botones, hagamos que cuando el usuario pulse sobre el mencionado botón, se escriba nuestro nombre automáticamente en el cuadro de texto correspondiente. Así, tecleamos:
def codigo_boton():
mi_nombre.set("Alexis")
root = Tk()
mi_nombre = StringVar()
La instrucción mi_nombre = StringVar() únicamente le indica a Python que la
variable mi_nombre es una cadena de caracteres. Como viene siendo habitual,
habremos de importar la correspondiente clase al comienzo del código (o,
directamente, utilizar el esquema from ... import *). Ahora, modificamos la
declaración del cuadro de texto asociado al nombre del usuario introduciendo el
parámetro textvariable:
cuadro_nombre = Entry(frame, textvariable=mi_nombre)
Nota técnica: no podemos utilizar StringVar() antes de definir la raíz
(root) de la aplicación.

Si la función set() nos permite declarar el valor de un cuadro de texto, para
obtener la información que el usuario introduzca en uno, utilizaremos, en
próximas lecciones, el método get().
Finalmente, comparto el código completo de esta última aplicación generada, para tener acceso así a una visión global de la misma:
from tkinter import Tk, Frame, Button, Entry, Label, Scrollbar, StringVar, Text
def codigo_boton():
mi_nombre.set("Alexis")
root = Tk()
mi_nombre = StringVar()
root.title("Probando los widgets Text y Button")
root.resizable(width=True, height=True)
root.iconbitmap("icon.ico")
root.config(bg="lightblue")
frame = Frame(root, width=450, height=300)
frame.pack()
nombre_label = Label(frame, text="Nombre:")
nombre_label.grid(row=0, column=0, sticky="e", padx=2, pady=2)
cuadro_nombre = Entry(frame, textvariable=mi_nombre)
cuadro_nombre.grid(row=0, column=1, padx=2, pady=2)
apellido_label = Label(frame, text="Apellido:")
apellido_label.grid(row=1, column=0, sticky="e", padx=2, pady=2)
apellido_texto = Entry(frame)
apellido_texto.grid(row=1, column=1, padx=2, pady=2)
direccion_label = Label(frame, text="Dirección:")
direccion_label.grid(row=2, column=0, sticky="e", padx=2, pady=2)
direccion_texto = Entry(frame)
direccion_texto.grid(row=2, column=1, padx=2, pady=2)
tfno_label = Label(frame, text="Teléfono (fijo):")
tfno_label.grid(row=3, column=0, sticky="e", padx=2, pady=2)
tfno_texto = Entry(frame)
tfno_texto.grid(row=3, column=1, padx=2, pady=2)
movil_label = Label(frame, text="Teléfono (móvil):")
movil_label.grid(row=4, column=0, sticky="e", padx=2, pady=2)
movil_texto = Entry(frame)
movil_texto.grid(row=4, column=1, padx=2, pady=2)
pass_label = Label(frame, text="Constraseña:")
pass_label.grid(row=5, column=0, sticky="e", padx=2, pady=2)
pass_texto = Entry(frame)
pass_texto.grid(row=5, column=1, padx=2, pady=2)
pass_texto.config(show="*")
bio_label = Label(frame, text="Biografía:")
bio_label.grid(row=6, column=0, sticky="e", padx=2, pady=2)
bio_texto = Text(frame, width=15, height=5)
bio_texto.grid(row=6, column=1, padx=2, pady=2)
scroll_vert = Scrollbar(frame, command=bio_texto.yview)
scroll_vert.grid(row=6, column=2, sticky="nsew")
bio_texto.config(yscrollcommand=scroll_vert.set)
boton_envio = Button(root, text="Enviar", command=codigo_boton)
boton_envio.pack()
root.mainloop()
6. Interfaces Gráficas VI
6.1. Vídeo
6.2. Notas personales
A partir de esta lección, utilizando los conocimientos adquiridos a lo largo de todo el curso, empezaremos un nuevo proyecto: la creación de una calculadora. Comencemos elaborando su interfaz gráfica:
from tkinter import Button, Entry, Frame, Tk
# Raíz
raiz = Tk()
raiz.title("Calculadora")
raiz.iconbitmap("icon.ico")
# Frame
frame = Frame(raiz)
frame.pack()
# Pantalla
pantalla = Entry(frame)
pantalla.grid(row=1, column=1, padx=10, pady=10, columnspan=4)
pantalla.config(background="black", fg="#03f943", justify="right")
# Fila 1 de botones
boton7 = Button(frame, text="7", width=3)
boton7.grid(row=2, column=1)
boton8 = Button(frame, text="8", width=3)
boton8.grid(row=2, column=2)
boton9 = Button(frame, text="9", width=3)
boton9.grid(row=2, column=3)
boton_div = Button(frame, text="/", width=3)
boton_div.grid(row=2, column=4)
# Fila 2 de botones
boton4 = Button(frame, text="4", width=3)
boton4.grid(row=3, column=1)
boton5 = Button(frame, text="5", width=3)
boton5.grid(row=3, column=2)
boton6 = Button(frame, text="6", width=3)
boton6.grid(row=3, column=3)
boton_mult = Button(frame, text="*", width=3)
boton_mult.grid(row=3, column=4)
# Fila 3 de botones
boton1 = Button(frame, text="1", width=3)
boton1.grid(row=4, column=1)
boton2 = Button(frame, text="2", width=3)
boton2.grid(row=4, column=2)
boton3 = Button(frame, text="3", width=3)
boton3.grid(row=4, column=3)
boton_rest = Button(frame, text="-", width=3)
boton_rest.grid(row=4, column=4)
# Fila 4 de botones
boton0 = Button(frame, text="0", width=3)
boton0.grid(row=5, column=1)
boton_coma = Button(frame, text=".", width=3)
boton_coma.grid(row=5, column=2)
boton_igual = Button(frame, text="=", width=3)
boton_igual.grid(row=5, column=3)
boton_suma = Button(frame, text="+", width=3)
boton_suma.grid(row=5, column=4)
raiz.mainloop()
Nota: la pantalla ha de ocupar no una columna, sino cuatro, ya que hemos
generado después filas de cuatro botones. Para ello, utilizamos el parámetro
columnspan en la función grid() correspondiente a la pantalla y le asignamos
el valor 4.
Interfaz gráfica de la calculadora.

7. Interfaces Gráficas VII
7.1. Vídeo
7.2. Notas personales
Una vez elaborada la interfaz gráfica de la calculadora, en esta lección abordaremos cómo programar parte de la funcionalidad de la misma. Para empezar, nuestro objetivo será conseguir que al pulsar los diferentes botones numéricos aparezcan sus valores asociados en la pantalla.
Empecemos creando una variable para almacenar una cadena de texto y asociémosla a la pantalla:
numero_pantalla = StringVar()
pantalla = Entry(frame, textvariable=numero_pantalla)
A continuación, creemos una función que, por ejemplo, escriba el número 4 en
pantalla:
# Pulsaciones teclado
def numero_pulsado():
numero_pantalla.set("4")
Ahora, asociémosla al botón correspondiente:
boton4 = Button(frame, text="4", width=3, command=numero_pulsado)
Ahora, al pulsar en el botón del número cuatro, aparece un 4 en la pantalla.
Vemos que si pulsamos en varias ocasiones, no se añaden más cuatros, que sería
el comportamiento deseable. Modifiquemos la función numero_pulsado() para
conseguir tal efecto. Para ello, obtendremos la información actual de la
pantalla y le agregaremos el número 4 después:
# Pulsaciones teclado
def numero_pulsado():
numero_pantalla.set(numero_pantalla.get() + "4")
Para generalizar, necesitaremos el uso de funciones lambda o anónimas en la
declaración de los botones. De no utilizarlas, tal y como transcurre el flujo
del programa, al llegar a la línea de la declaración de boton4, se produciría
directamente la llamada de la función numero_pulsado("4"), mostrando (sin que
el usuario pulse sobre nada) un 4 en la pantalla al abrir la calculadura y,
además, deshabilitando la funcionalidad del botón, puesto que al seguir pulsando
sobre él no añade más cuatros.
def numero_pulsado(num):
numero_pantalla.set(numero_pantalla.get() + num)
boton4 = Button(frame, text="4", width=3, command=lambda: numero_pulsado("4"))
Para finalizar, incluyo el código completo de la aplicación elaborada hasta este instante, para obtener así una visión global de la calculadora:
from tkinter import Button, Entry, Frame, StringVar, Tk
# Raíz
raiz = Tk()
raiz.title("Calculadora")
raiz.iconbitmap("icon.ico")
# Frame
frame = Frame(raiz)
frame.pack()
# Variables
numero_pantalla = StringVar()
# Pantalla
pantalla = Entry(frame, textvariable=numero_pantalla)
pantalla.grid(row=1, column=1, padx=10, pady=10, columnspan=4)
pantalla.config(background="black", fg="#03f943", justify="right")
# Pulsaciones teclado
def numero_pulsado(num):
numero_pantalla.set(numero_pantalla.get() + num)
# Fila 1 de botones
boton7 = Button(frame, text="7", width=3, command=lambda: numero_pulsado("7"))
boton7.grid(row=2, column=1)
boton8 = Button(frame, text="8", width=3, command=lambda: numero_pulsado("8"))
boton8.grid(row=2, column=2)
boton9 = Button(frame, text="9", width=3, command=lambda: numero_pulsado("9"))
boton9.grid(row=2, column=3)
boton_div = Button(frame, text="/", width=3)
boton_div.grid(row=2, column=4)
# Fila 2 de botones
boton4 = Button(frame, text="4", width=3, command=lambda: numero_pulsado("4"))
boton4.grid(row=3, column=1)
boton5 = Button(frame, text="5", width=3, command=lambda: numero_pulsado("5"))
boton5.grid(row=3, column=2)
boton6 = Button(frame, text="6", width=3, command=lambda: numero_pulsado("6"))
boton6.grid(row=3, column=3)
boton_mult = Button(frame, text="*", width=3)
boton_mult.grid(row=3, column=4)
# Fila 3 de botones
boton1 = Button(frame, text="1", width=3, command=lambda: numero_pulsado("1"))
boton1.grid(row=4, column=1)
boton2 = Button(frame, text="2", width=3, command=lambda: numero_pulsado("2"))
boton2.grid(row=4, column=2)
boton3 = Button(frame, text="3", width=3, command=lambda: numero_pulsado("3"))
boton3.grid(row=4, column=3)
boton_rest = Button(frame, text="-", width=3)
boton_rest.grid(row=4, column=4)
# Fila 4 de botones
boton0 = Button(frame, text="0", width=3, command=lambda: numero_pulsado("0"))
boton0.grid(row=5, column=1)
boton_coma = Button(frame,
text=".",
width=3,
command=lambda: numero_pulsado("."))
boton_coma.grid(row=5, column=2)
boton_igual = Button(frame, text="=", width=3)
boton_igual.grid(row=5, column=3)
boton_suma = Button(frame, text="+", width=3)
boton_suma.grid(row=5, column=4)
raiz.mainloop()
8. Interfaces Gráficas VIII
8.1. Vídeo
8.2. Notas personales
En esta lección, nuestro objetivo será conseguir que la calculadora que estamos generando sea capaz de sumar valores numéricos enteros.
Empecemos declarando una variable global, que será accesible desde todos las
funciones del programa, denominada operacion y que almacenará la operación
aritmética que desea el usuario llevar a cabo. Además, la utilizaremos para
conseguir que la pantalla vuelva a su estado inicial a través del uso de bloques
condicionales.
operación = ""
Esta variable cambiará de valor a medida que pulsemos los botones de operaciones aritméticas. Por ejemplo, para la suma, definimos la función:
# Función suma
def suma():
global operacion
operacion = "suma"
A continuación, modificamos el código de boton_suma como sigue:
boton_suma = Button(frame, text="+", width=3, command=lambda: suma())
Acto seguido, hemos de conseguir que, cuando se pulse dicho botón, la pantalla
se borre y permita el almacenamiento de un nuevo número. Modifiquemos la función
numero_pulsado():
def numero_pulsado(num):
global operacion
if operacion != "":
numero_pantalla.set(num)
operacion = ""
else:
numero_pantalla.set(numero_pantalla.get() + num)
Si pulsamos sobre el botón de sumar, la condición operacion != "" sería
cierta, por lo que entraríamos en esa parte de la estructura condicional. En su
interior, apreciamos que no concatenamos el número con nada más y volvemos a
declarar el valor de la variable operacion como una cadena vacía, para
permitir así la correcta introducción de un nuevo número.
Ahora necesitamos una variable global, que denominaremos resultado, que vaya
almacenando los valores introducidos:
resultado = 0
La siguiente tarea consiste en actualizar la función suma():
# Función suma
def suma(num):
global operacion, resultado
operacion = "suma"
resultado += int(num)
numero_pantalla.set(resultado)
donde num representa el número que aparece en la pantalla de la calculadora al
pulsar el botón de sumar. Como estamos trabajando con cuadros de texto, Python
almacena los textos, lógicamente como su nombre indica, como cadenas de
caracteres, de ahí la necesidad de utilizar la función int(). Una vez
realizada la operación aritmética, mostramos su resultado en la pantalla con
numero_pantalla.set(resultado).
El nuevo parámetro de la función suma() nos obliga a modificar el código de
boton_suma:
boton_suma = Button(frame,
text="+",
width=3,
command=lambda: suma(numero_pantalla.get()))
A continuación, programemos el comportamiento del botón del símbolo igual.
# Función el_resultado (para el botón igual)
def el_resultado():
global resultado
numero_pantalla.set(resultado + int(numero_pantalla.get()))
resultado = 0
Es decir, al resultado acumulado hemos de sumarle el número que figure en
pantalla antes de pulsar el botón del símbolo igual. Tras ello, reseteamos la
variable resultado.
Procedamos ahora a modificar el código el mencionado botón:
boton_igual = Button(frame, text="=", width=3, command=lambda: el_resultado())
Finalmente, como viene siendo habitual, comparto el código completo de la aplicación para que podamos tener una visión global de la calculadora:
from tkinter import Button, Entry, Frame, StringVar, Tk
# Raíz
raiz = Tk()
raiz.title("Calculadora")
raiz.iconbitmap("icon.ico")
# Frame
frame = Frame(raiz)
frame.pack()
# Variables
numero_pantalla = StringVar()
operacion = ""
resultado = 0
# Pantalla
pantalla = Entry(frame, textvariable=numero_pantalla)
pantalla.grid(row=1, column=1, padx=10, pady=10, columnspan=4)
pantalla.config(background="black", fg="#03f943", justify="right")
# Pulsaciones teclado
def numero_pulsado(num):
global operacion
if operacion != "":
numero_pantalla.set(num)
operacion = ""
else:
numero_pantalla.set(numero_pantalla.get() + num)
# Función suma
def suma(num):
global operacion, resultado
operacion = "suma"
resultado += int(num)
numero_pantalla.set(resultado)
# Función el_resultado (para el botón igual)
def el_resultado():
global resultado
numero_pantalla.set(resultado + int(numero_pantalla.get()))
resultado = 0
# Fila 1 de botones
boton7 = Button(frame, text="7", width=3, command=lambda: numero_pulsado("7"))
boton7.grid(row=2, column=1)
boton8 = Button(frame, text="8", width=3, command=lambda: numero_pulsado("8"))
boton8.grid(row=2, column=2)
boton9 = Button(frame, text="9", width=3, command=lambda: numero_pulsado("9"))
boton9.grid(row=2, column=3)
boton_div = Button(frame, text="/", width=3)
boton_div.grid(row=2, column=4)
# Fila 2 de botones
boton4 = Button(frame, text="4", width=3, command=lambda: numero_pulsado("4"))
boton4.grid(row=3, column=1)
boton5 = Button(frame, text="5", width=3, command=lambda: numero_pulsado("5"))
boton5.grid(row=3, column=2)
boton6 = Button(frame, text="6", width=3, command=lambda: numero_pulsado("6"))
boton6.grid(row=3, column=3)
boton_mult = Button(frame, text="*", width=3)
boton_mult.grid(row=3, column=4)
# Fila 3 de botones
boton1 = Button(frame, text="1", width=3, command=lambda: numero_pulsado("1"))
boton1.grid(row=4, column=1)
boton2 = Button(frame, text="2", width=3, command=lambda: numero_pulsado("2"))
boton2.grid(row=4, column=2)
boton3 = Button(frame, text="3", width=3, command=lambda: numero_pulsado("3"))
boton3.grid(row=4, column=3)
boton_rest = Button(frame, text="-", width=3)
boton_rest.grid(row=4, column=4)
# Fila 4 de botones
boton0 = Button(frame, text="0", width=3, command=lambda: numero_pulsado("0"))
boton0.grid(row=5, column=1)
boton_coma = Button(frame,
text=".",
width=3,
command=lambda: numero_pulsado("."))
boton_coma.grid(row=5, column=2)
boton_igual = Button(frame, text="=", width=3, command=lambda: el_resultado())
boton_igual.grid(row=5, column=3)
boton_suma = Button(frame,
text="+",
width=3,
command=lambda: suma(numero_pantalla.get()))
boton_suma.grid(row=5, column=4)
raiz.mainloop()
9. Interfaces Gráficas IX
9.1. Vídeo
9.2. Notas personales
Partamos, en esta lección, del siguiente código fuente, que incluye, además de la función para sumar, las correspondientes a las operaciones resta, multiplicación y división:
from tkinter import Button, Entry, Frame, StringVar, Tk
# Raíz
raiz = Tk()
raiz.title("Calculadora")
raiz.iconbitmap("icon.ico")
# Frame
frame = Frame(raiz)
frame.pack()
# Variables
numero_pantalla = StringVar()
operacion = ""
resultado = 0
reset_pantalla = False
# Pantalla
pantalla = Entry(frame, textvariable=numero_pantalla)
pantalla.grid(row=1, column=1, padx=10, pady=10, columnspan=4)
pantalla.config(background="black", fg="#03f943", justify="right")
# Pulsaciones teclado
def numero_pulsado(num):
global operacion, reset_pantalla
if reset_pantalla:
numero_pantalla.set(num)
reset_pantalla = False
else:
numero_pantalla.set(numero_pantalla.get() + num)
# Función suma
def suma(num):
global operacion, reset_pantalla, resultado
operacion = "suma"
resultado += int(num)
reset_pantalla = True
numero_pantalla.set(resultado)
# Función resta
num1 = 0
contador_resta = 0
def resta(num):
global contador_resta, num1, operacion, reset_pantalla, resultado
if contador_resta == 0:
num1 = int(num)
resultado = num1
else:
if contador_resta == 1:
resultado = num1 - int(num)
else:
resultado = int(resultado) - int(num)
numero_pantalla.set(resultado)
resultado = numero_pantalla.get()
contador_resta = contador_resta + 1
operacion = "resta"
reset_pantalla = True
# Función multiplicación
contador_multi = 0
def multiplica(num):
global contador_multi, num1, operacion, reset_pantalla, resultado
if contador_multi == 0:
num1 = int(num)
resultado = num1
else:
if contador_multi == 1:
resultado = num1 * int(num)
else:
resultado = int(resultado) * int(num)
numero_pantalla.set(resultado)
resultado = numero_pantalla.get()
contador_multi = contador_multi + 1
operacion = "multiplicacion"
reset_pantalla = True
# Función división
contador_divi = 0
def divide(num):
global contador_divi, num1, operacion, reset_pantalla, resultado
if contador_divi == 0:
num1 = float(num)
resultado = num1
else:
if contador_resta == 1:
resultado = num1 / float(num)
else:
resultado = float(resultado) / float(num)
numero_pantalla.set(resultado)
resultado = numero_pantalla.get()
contador_divi = contador_divi + 1
operacion = "division"
reset_pantalla = True
# Función el_resultado (para el botón igual)
def el_resultado():
global contador_divi, contador_multi, contador_resta, operacion, resultado
if operacion == "suma":
numero_pantalla.set(resultado + int(numero_pantalla.get()))
resultado = 0
elif operacion == "resta":
numero_pantalla.set(int(resultado) - int(numero_pantalla.get()))
resultado = 0
contador_resta = 0
elif operacion == "multiplicacion":
numero_pantalla.set(int(resultado) * int(numero_pantalla.get()))
resultado = 0
contador_multi = 0
elif operacion == "division":
numero_pantalla.set(int(resultado) / int(numero_pantalla.get()))
resultado = 0
contador_divi = 0
# Fila 1 de botones
boton7 = Button(frame, text="7", width=3, command=lambda: numero_pulsado("7"))
boton7.grid(row=2, column=1)
boton8 = Button(frame, text="8", width=3, command=lambda: numero_pulsado("8"))
boton8.grid(row=2, column=2)
boton9 = Button(frame, text="9", width=3, command=lambda: numero_pulsado("9"))
boton9.grid(row=2, column=3)
boton_div = Button(frame,
text="/",
width=3,
command=lambda: divide(numero_pantalla.get()))
boton_div.grid(row=2, column=4)
# Fila 2 de botones
boton4 = Button(frame, text="4", width=3, command=lambda: numero_pulsado("4"))
boton4.grid(row=3, column=1)
boton5 = Button(frame, text="5", width=3, command=lambda: numero_pulsado("5"))
boton5.grid(row=3, column=2)
boton6 = Button(frame, text="6", width=3, command=lambda: numero_pulsado("6"))
boton6.grid(row=3, column=3)
boton_mult = Button(frame,
text="*",
width=3,
command=lambda: multiplica(numero_pantalla.get()))
boton_mult.grid(row=3, column=4)
# Fila 3 de botones
boton1 = Button(frame, text="1", width=3, command=lambda: numero_pulsado("1"))
boton1.grid(row=4, column=1)
boton2 = Button(frame, text="2", width=3, command=lambda: numero_pulsado("2"))
boton2.grid(row=4, column=2)
boton3 = Button(frame, text="3", width=3, command=lambda: numero_pulsado("3"))
boton3.grid(row=4, column=3)
boton_rest = Button(frame,
text="-",
width=3,
command=lambda: resta(numero_pantalla.get()))
boton_rest.grid(row=4, column=4)
# Fila 4 de botones
boton0 = Button(frame, text="0", width=3, command=lambda: numero_pulsado("0"))
boton0.grid(row=5, column=1)
boton_coma = Button(frame,
text=".",
width=3,
command=lambda: numero_pulsado("."))
boton_coma.grid(row=5, column=2)
boton_igual = Button(frame, text="=", width=3, command=lambda: el_resultado())
boton_igual.grid(row=5, column=3)
boton_suma = Button(frame,
text="+",
width=3,
command=lambda: suma(numero_pantalla.get()))
boton_suma.grid(row=5, column=4)
raiz.mainloop()
A continuación, veremos cómo trabajar con botones de radio, es decir, con la
clase Radiobutton.
from tkinter import Radiobutton, Tk
raiz = Tk()
raiz.title("Botones de radio")
raiz.iconbitmap("icon.ico")
Radiobutton(raiz, text="Femenino").pack()
Radiobutton(raiz, text="Masculino").pack()
raiz.mainloop()

A primera vista, observamos dos inconvenientes:
- aparecen ambas opciones seleccionadas al abrir la aplicación y
- por mucho que pulse sobre ellas, la selección no se modifica.
Para abordar esta situación, comenzamos creando una variable global, opcion,
perteneciente a la clase IntVar y se la asignamos a ambos botones a través del
parámetro variable; junto con un valor para cada uno de ellos, utilizando el
parámetro value.
from tkinter import IntVar, Radiobutton, Tk
raiz = Tk()
raiz.title("Botones de radio")
raiz.iconbitmap("icon.ico")
opcion = IntVar()
Radiobutton(raiz, text="Femenino", variable=opcion, value=1).pack()
Radiobutton(raiz, text="Masculino", variable=opcion, value=2).pack()
raiz.mainloop()

Nota técnica: si asignamos value=0 a alguno de los botones, aparecerá
seleccionado por defecto cuando abramos la aplicación. Esta característica puede
resultar de cierta utilidad en algunos contextos.
Ahora bien, ¿cómo rescatamos el valor que ha seleccionado el usuario? Al igual que sucedía en el caso de la calculadora, recurriremos al uso de funciones en esta ocasión.
Así, generemos una, denominada imprimir, que imprima en la consola de Python
el valor del botón seleccionado:
from tkinter import IntVar, Label, Radiobutton, Tk
raiz = Tk()
raiz.title("Botones de radio")
raiz.iconbitmap("icon.ico")
opcion = IntVar()
def imprimir():
print(opcion.get())
Label(raiz, text="Género:").pack()
Radiobutton(raiz, text="Femenino", variable=opcion, value=1,
command=imprimir).pack()
Radiobutton(raiz, text="Masculino", variable=opcion, value=2,
command=imprimir).pack()
raiz.mainloop()
Modifiquemos el código para ver dichos valores en la propia interfaz:
from tkinter import IntVar, Label, Radiobutton, Tk
raiz = Tk()
raiz.title("Botones de radio")
raiz.iconbitmap("icon.ico")
opcion = IntVar()
def imprimir():
if opcion.get() == 1:
etiqueta.config(text="Has elegido género femenino.")
else:
etiqueta.config(text="Has elegido género masculino.")
Label(raiz, text="Género:").pack()
Radiobutton(raiz, text="Femenino", variable=opcion, value=1,
command=imprimir).pack()
Radiobutton(raiz, text="Masculino", variable=opcion, value=2,
command=imprimir).pack()
etiqueta = Label(raiz)
etiqueta.pack()
raiz.mainloop()

Incorporar un botón adicional que contemple otros géneros es sencillo:
from tkinter import IntVar, Label, Radiobutton, Tk
raiz = Tk()
raiz.title("Botones de radio")
raiz.iconbitmap("icon.ico")
opcion = IntVar()
def imprimir():
if opcion.get() == 1:
etiqueta.config(text="Has elegido género femenino.")
elif opcion.get() == 2:
etiqueta.config(text="Has elegido género masculino.")
else:
etiqueta.config(text="Has elegido otras opciones para género.")
Label(raiz, text="Género:").pack()
Radiobutton(raiz, text="Femenino", variable=opcion, value=1,
command=imprimir).pack()
Radiobutton(raiz, text="Masculino", variable=opcion, value=2,
command=imprimir).pack()
Radiobutton(raiz, text="Otras opciones", variable=opcion, value=3,
command=imprimir).pack()
etiqueta = Label(raiz)
etiqueta.pack()
raiz.mainloop()

10. Interfaces Gráficas X
10.1. Vídeo
10.2. Notas personales
En esta lección, introduciremos el funcionamiento de la clase Checkbutton, que
se encarga de gestionar los clásicos botones de selección (también denominados
casillas de verificación). Estos nos permiten la posibilidad de realizar una
selección múltiple sobre distintas opciones ofrecidas.
Acto seguido, veamos un sencillo ejemplo de aplicación de la mencionada clase, donde el usuario ha de escoger qué tipo de destinos prefiere para sus vacaciones.
from tkinter import Checkbutton, Frame, IntVar, Label, PhotoImage, Tk
raiz = Tk()
raiz.title("Casillas de verificación")
raiz.iconbitmap("icon.ico")
playa = IntVar()
montana = IntVar()
turismo_rural = IntVar()
def opciones_viaje():
opcion_escogida = ""
if playa.get() == 1:
opcion_escogida += " playa"
if montana.get() == 1:
opcion_escogida += " montaña"
if turismo_rural.get() == 1:
opcion_escogida += " turismo rural"
texto_final.config(text=opcion_escogida)
foto = PhotoImage(file="helicoptero.png")
Label(raiz, image=foto).pack()
frame = Frame(raiz)
frame.pack()
Label(frame, text="Escoge destinos:", width=50).pack()
Checkbutton(frame,
text="Playa",
variable=playa,
onvalue=1,
offvalue=0,
command=opciones_viaje).pack()
Checkbutton(frame,
text="Montaña",
variable=montana,
onvalue=1,
offvalue=0,
command=opciones_viaje).pack()
Checkbutton(frame,
text="Turismo rural",
variable=turismo_rural,
onvalue=1,
offvalue=0,
command=opciones_viaje).pack()
texto_final = Label(frame)
texto_final.pack()
raiz.mainloop()
Para empezar, el siguiente bloque de código
foto = PhotoImage(file="helicoptero.png")
Label(raiz, image=foto).pack()
nos permite introducir, como cabecera de nuestra aplicación, la imagen de un helicóptero (disponible en este enlace).
A continuación, las líneas de código asociadas a la primera casilla de verificación son
Checkbutton(frame,
text="Playa",
variable=playa,
onvalue=1,
offvalue=0,
command=opciones_viaje).pack()
Observamos que dicha casilla está ubicada en el frame frame y muestra como
texto, en la ventana de la aplicación, la palabra Playa. Para posibilitar la
interacción con ella, almacenamos su valor en la variable playa, siendo este
1 cuando esté seleccionada y 0 en otro caso. Finalmente, su comportamiento
se gestiona a través de la función opciones_viaje(), tal y como figura en el
valor del parámetro command.
Por lo que respecta a la variable playa, así como al resto, las declaramos
utilizando la clase IntVar, puesto que nuestra intención es almacenar en ellas
valores enteros:
playa = IntVar()
montana = IntVar()
turismo_rural = IntVar()
Por otro lado, mostraremos las opciones seleccionadas por el usuario empleando una etiqueta para ello, de ahí que figure el siguiente bloque de código al final del programa:
texto_final = Label(frame)
texto_final.pack()
Finalmente, la función que gestiona el comportamiento de las casillas de
verificación simplemente se ocupa de establecer el texto de la variable
texto_final según una serie de bloques condicionales:
def opciones_viaje():
opcion_escogida = ""
if playa.get() == 1:
opcion_escogida += " playa"
if montana.get() == 1:
opcion_escogida += " montaña"
if turismo_rural.get() == 1:
opcion_escogida += " turismo rural"
texto_final.config(text=opcion_escogida)

11. Interfaces Gráficas XI
11.1. Vídeo
11.2. Notas personales
En esta lección, abordaremos el estudio del widget Menu, que nos permitirá
la posibilidad de crear barras de menús.
Comencemos utilizando el siguiente esqueleto de aplicación:
from tkinter import Tk
raiz = Tk()
raiz.title("Menús")
raiz.iconbitmap("icon.ico")
raiz.mainloop()
A continuación, generamos una variable, barra_menu, que será la encargada de
almacenar el menú:
barra_menu = Menu(raiz)
y configuramos el valor del parámetro menu de la raíz para que figure en
nuestra aplicación:
raiz.config(menu=barra_menu)
Acto seguido, establecemos los elementos que conformarán nuestro menú. Por
ejemplo, para crear uno denominado Archivo, generamos la variable
archivo_menu, indicándole que pertenece a la barra de menús barra_menu.
archivo_menu = Menu(barra_menu)
No obstante, si ejecutamos el código, todavía no aparece barra de menús alguna. Añadamos el texto correspondiente a cada elemento. Así,
barra_menu.add_cascade(label="Archivo", menu=archivo_menu)
Ahora, ya únicamente nos resta la tarea de añadir elementos de submenú. Para
ello, tras la línea de declaración del elemento archivo_menu, tecleamos:
archivo_menu.add_command(label="Nuevo")
archivo_menu.add_command(label="Abrir")
archivo_menu.add_command(label="Guardar")
archivo_menu.add_command(label="Guardar como...")
archivo_menu.add_command(label="Cerrar")
archivo_menu.add_command(label="Salir")
Aparece una barra separadora, que identificamos por los símbolos - - - -, al
pulsar sobre cualquier elemento de la barra de menús. Para suprimirla,
modificamos como sigue la línea de declaración de la variable archivo_menu:
archivo_menu = Menu(barra_menu, tearoff=0)
No obstante, aunque la mencionada barra separada no nos interesaba, sí que podemos desear diferenciar, de alguna manera, los diversos elementos de un submenú, para que así queden agrupados por cierto criterio. Con tal objetivo, donde nos convenga, introducimos la instrucción
archivo_menu.add_separator()
Para acabar, comparto el código completo de la aplicación, para obtener así una
visión global del funcionamiento de la clase Menu.
from tkinter import Menu, Tk
raiz = Tk()
raiz.title("Menús")
raiz.iconbitmap("icon.ico")
barra_menu = Menu(raiz)
raiz.config(menu=barra_menu, width=300, height=300)
archivo_menu = Menu(barra_menu, tearoff=0)
archivo_menu.add_command(label="Nuevo")
archivo_menu.add_command(label="Abrir")
archivo_menu.add_separator()
archivo_menu.add_command(label="Guardar")
archivo_menu.add_command(label="Guardar como...")
archivo_menu.add_separator()
archivo_menu.add_command(label="Cerrar")
archivo_menu.add_command(label="Salir")
edicion_menu = Menu(barra_menu, tearoff=0)
edicion_menu.add_command(label="Copiar")
edicion_menu.add_command(label="Cortar")
edicion_menu.add_command(label="Pegar")
herramientas_menu = Menu(barra_menu, tearoff=0)
ayuda_menu = Menu(barra_menu, tearoff=0)
ayuda_menu.add_command(label="Licencia")
ayuda_menu.add_separator()
ayuda_menu.add_command(label="Acerca de...")
barra_menu.add_cascade(label="Archivo", menu=archivo_menu)
barra_menu.add_cascade(label="Edición", menu=edicion_menu)
barra_menu.add_cascade(label="Herramientas", menu=herramientas_menu)
barra_menu.add_cascade(label="Ayuda", menu=ayuda_menu)
raiz.mainloop()

12. Interfaces Gráficas XII
12.1. Vídeo
12.2. Notas personales
En esta lección, estudiaremos cómo construir ventanas emergentes, que son ventanas modales para informar, avisar o permitir realizar ciertas tareas al usuario.
Para comenzar, recuperemos el código fuente generado en la lección anterior:
from tkinter import Menu, Tk
raiz = Tk()
raiz.title("Menús")
raiz.iconbitmap("icon.ico")
barra_menu = Menu(raiz)
raiz.config(menu=barra_menu, width=300, height=300)
archivo_menu = Menu(barra_menu, tearoff=0)
archivo_menu.add_command(label="Nuevo")
archivo_menu.add_command(label="Abrir")
archivo_menu.add_separator()
archivo_menu.add_command(label="Guardar")
archivo_menu.add_command(label="Guardar como...")
archivo_menu.add_separator()
archivo_menu.add_command(label="Cerrar")
archivo_menu.add_command(label="Salir")
edicion_menu = Menu(barra_menu, tearoff=0)
edicion_menu.add_command(label="Copiar")
edicion_menu.add_command(label="Cortar")
edicion_menu.add_command(label="Pegar")
herramientas_menu = Menu(barra_menu, tearoff=0)
ayuda_menu = Menu(barra_menu, tearoff=0)
ayuda_menu.add_command(label="Licencia")
ayuda_menu.add_separator()
ayuda_menu.add_command(label="Acerca de...")
barra_menu.add_cascade(label="Archivo", menu=archivo_menu)
barra_menu.add_cascade(label="Edición", menu=edicion_menu)
barra_menu.add_cascade(label="Herramientas", menu=herramientas_menu)
barra_menu.add_cascade(label="Ayuda", menu=ayuda_menu)
raiz.mainloop()
A continuación, nuestro objetivo será construir una ventana emergente que aparezca cuando el usuario pulse sobre la opción Acerca de…, ubicada en el menú Ayuda. Para ello, empecemos creando una función que será la responsable de generar la mencionada ventana emergente:
def info_adicional():
messagebox.showinfo(title="Acerca de...",
message="Aplicación creada por Alexis Sáez.")
Notas:
- Hemos de importar el módulo
messageboxal principio de nuestro código. - El parámetro
titlegestiona el texto que aparecerá en la barra que figura en la parte superior de la ventana emergente. - El parámetro
textdeclara el texto que se ubicará en el cuerpo de la ventana emergente.
Acto seguido, modificamos la declaración del submenú Acerca de… como sigue:
ayuda_menu.add_command(label="Acerca de...", command=info_adicional)

Estas ventanas emergentes admiten enormes posibilidades de configuración y podemos adaptarlas según nuestra intención sea informar al usuario de algún detalle concreto (como el ejemplo que se muestra en la imagen que figura arriba), avisarle de algún error, etc. Los símbolos y la disposición de los diferentes botones asociados variarían en función de nuestro objetivo.
Por ejemplo, generemos una ventana emergente que nos avise del estado de la licencia de nuestra aplicación. Para ello, creamos la siguiente función:
def aviso_licencia():
messagebox.showwarning(title="Licencia",
message="Producto bajo licencia GNU.")
y modificamos la correspondiente opción del menú Ayuda:
ayuda_menu.add_command(label="Licencia", command=aviso_licencia)

Acto seguido, veamos un nuevo tipo de ventana emergente, que asociaremos al submenú Salir y que nos pedirá confirmación antes de proceder a cerrar la aplicación. Con tal objetivo en mente, empecemos construyendo la función:
def salir_aplicacion():
messagebox.askquestion(title="Salir",
message="¿Desear salir de la aplicación?")
para luego modificar la opción del menú Archivo asociada:
archivo_menu.add_command(label="Salir", command=salir_aplicacion)

Ahora, si pulsamos sobre el botón No, volvemos a la aplicación, como cabría esperar. No obstante, al pulsar sobre el botón Sí debería salir de la aplicación y no sucede tal acción, puesto que hemos de programar todavía dicho comportamiento.
La función askquestion() devuelve una cadena de texto en función del botón
pulsado, "yes" o "no", por lo que basta modificar la función
salir_aplicacion() como sigue:
def salir_aplicacion():
respuesta = messagebox.askquestion(
title="Salir", message="¿Desear salir de la aplicación?")
if respuesta == "yes":
raiz.destroy()
La función destroy() posee un nombre lo suficientemente explicativo para que
intuyamos cómo afecta a la raíz de la aplicación.
Nota: con la función askokcancel() tenemos una variante de la anterior
ventana emergente, cuyos botones son del tipo Aceptar y Cancelar.
A continuación, veamos una ventana emergente de tipo ‘‘reintentar’’, asociada a la opción Cerrar del menú Archivo. Para ello, construimos la siguiente función:
def cerrar_documento():
messagebox.askretrycancel(
title="Reintentar",
message="No es posible cerrar. Documento bloqueado.")
y modificamos el correspondiente elemento del menú Archivo:
archivo_menu.add_command(label="Cerrar", command=cerrar_documento)

Finalmente, como viene siendo habitual en este subapartado del curso dedicado a las interfaces gráficas, comparto el código completo de la aplicación generada para así ofrecer una visión global de la misma.
from tkinter import Menu, messagebox, Tk
def info_adicional():
messagebox.showinfo(title="Acerca de...",
message="Aplicación creada por Alexis Sáez.")
def aviso_licencia():
messagebox.showwarning(title="Licencia",
message="Producto bajo licencia GNU.")
def salir_aplicacion():
respuesta = messagebox.askquestion(
title="Salir", message="¿Desear salir de la aplicación?")
if respuesta == "yes":
raiz.destroy()
def cerrar_documento():
messagebox.askretrycancel(
title="Reintentar",
message="No es posible cerrar. Documento bloqueado.")
raiz = Tk()
raiz.title("Menús")
raiz.iconbitmap("icon.ico")
barra_menu = Menu(raiz)
raiz.config(menu=barra_menu, width=300, height=300)
archivo_menu = Menu(barra_menu, tearoff=0)
archivo_menu.add_command(label="Nuevo")
archivo_menu.add_command(label="Abrir")
archivo_menu.add_separator()
archivo_menu.add_command(label="Guardar")
archivo_menu.add_command(label="Guardar como...")
archivo_menu.add_separator()
archivo_menu.add_command(label="Cerrar", command=cerrar_documento)
archivo_menu.add_command(label="Salir", command=salir_aplicacion)
edicion_menu = Menu(barra_menu, tearoff=0)
edicion_menu.add_command(label="Copiar")
edicion_menu.add_command(label="Cortar")
edicion_menu.add_command(label="Pegar")
herramientas_menu = Menu(barra_menu, tearoff=0)
ayuda_menu = Menu(barra_menu, tearoff=0)
ayuda_menu.add_command(label="Licencia", command=aviso_licencia)
ayuda_menu.add_separator()
ayuda_menu.add_command(label="Acerca de...", command=info_adicional)
barra_menu.add_cascade(label="Archivo", menu=archivo_menu)
barra_menu.add_cascade(label="Edición", menu=edicion_menu)
barra_menu.add_cascade(label="Herramientas", menu=herramientas_menu)
barra_menu.add_cascade(label="Ayuda", menu=ayuda_menu)
raiz.mainloop()
13. Interfaces Gráficas XIII
13.1. Vídeo
13.2. Notas personales
En esta lección, construiremos la típica ventana emergente que nos permite abrir un archivo en una aplicación. Para empezar, recuperemos el código fuente generado en las últimas lecciones:
from tkinter import Menu, messagebox, Tk
def info_adicional():
messagebox.showinfo(title="Acerca de...",
message="Aplicación creada por Alexis Sáez.")
def aviso_licencia():
messagebox.showwarning(title="Licencia",
message="Producto bajo licencia GNU.")
def salir_aplicacion():
respuesta = messagebox.askquestion(
title="Salir", message="¿Desear salir de la aplicación?")
if respuesta == "yes":
raiz.destroy()
def cerrar_documento():
messagebox.askretrycancel(
title="Reintentar",
message="No es posible cerrar. Documento bloqueado.")
raiz = Tk()
raiz.title("Menús")
raiz.iconbitmap("icon.ico")
barra_menu = Menu(raiz)
raiz.config(menu=barra_menu, width=300, height=300)
archivo_menu = Menu(barra_menu, tearoff=0)
archivo_menu.add_command(label="Nuevo")
archivo_menu.add_command(label="Abrir")
archivo_menu.add_separator()
archivo_menu.add_command(label="Guardar")
archivo_menu.add_command(label="Guardar como...")
archivo_menu.add_separator()
archivo_menu.add_command(label="Cerrar", command=cerrar_documento)
archivo_menu.add_command(label="Salir", command=salir_aplicacion)
edicion_menu = Menu(barra_menu, tearoff=0)
edicion_menu.add_command(label="Copiar")
edicion_menu.add_command(label="Cortar")
edicion_menu.add_command(label="Pegar")
herramientas_menu = Menu(barra_menu, tearoff=0)
ayuda_menu = Menu(barra_menu, tearoff=0)
ayuda_menu.add_command(label="Licencia", command=aviso_licencia)
ayuda_menu.add_separator()
ayuda_menu.add_command(label="Acerca de...", command=info_adicional)
barra_menu.add_cascade(label="Archivo", menu=archivo_menu)
barra_menu.add_cascade(label="Edición", menu=edicion_menu)
barra_menu.add_cascade(label="Herramientas", menu=herramientas_menu)
barra_menu.add_cascade(label="Ayuda", menu=ayuda_menu)
raiz.mainloop()
A continuación, importamos el módulo filedialog, de la librería tkinter, y
construimos la siguiente función:
def abrir_archivo():
fichero = filedialog.askopenfilename(title="Abrir archivo")
print(fichero)
En la variable fichero, almacenamos la ruta al archivo que seleccionemos a
través de la ventana emergente, como bien podremos comprobar en la consola de
Python gracias a la función print() que hemos incorporado en el interior de
abrir_archivo(). Después, acudimos al elemento del menú Archivo
correspondiente y modificamos la línea como sigue:
archivo_menu.add_command(label="Abrir", command=abrir_archivo)
A la vista del resultado, es posible que nos interese modificar la ubicación de
la ruta desde la que un usuario ha de comenzar la búsqueda de un archivo. Para
ello, modificamos la función abrir_archivo() como sigue:
def abrir_archivo():
fichero = filedialog.askopenfilename(title="Abrir archivo",
initialdir="/")
print(fichero)
y, de esta manera, ahora la ventana emergente nos muestra los directorios ubicados en la raíz de nuestro disco duro.
Además, podemos restringir el tipo de archivo que deseamos un usuario examine
(por ejemplo, restringir la búsqueda a imágenes o documentos) mediante el
parámetro filetypes. Así, si modificamos la función abrir_archivo() como
sigue:
def abrir_archivo():
fichero = filedialog.askopenfilename(title="Abrir archivo",
initialdir="/",
filetypes=(("Ficheros de Excel",
"*.xlsx"),
("Ficheros de texto",
"*.txt")))
print(fichero)
ahora la ventana emergente nos restringe el tipo de fichero que podemos seleccionar y, además, nos permite filtrar por dos opciones diferentes (de Excel o de texto), según sus correspondientes extensiones.
Para comodidad del usuario, conviene siempre incluir una opción para abrir cualquier tipo de archivo, independientemente de su extensión:
def abrir_archivo():
fichero = filedialog.askopenfilename(
title="Abrir archivo",
initialdir="/",
filetypes=(("Ficheros de Excel", "*.xlsx"),
("Ficheros de texto", "*.txt"),
("Todos los archivos", "*.*")))
print(fichero)
Finalmente, como ya viene siendo habitual en esta serie de lecciones dedicadas a las interfaces gráficas, comparto el código fuente completo de la aplicación generada, para tener así una visión global de la misma:
from tkinter import filedialog, Menu, messagebox, Tk
def info_adicional():
messagebox.showinfo(title="Acerca de...",
message="Aplicación creada por Alexis Sáez.")
def aviso_licencia():
messagebox.showwarning(title="Licencia",
message="Producto bajo licencia GNU.")
def salir_aplicacion():
respuesta = messagebox.askquestion(
title="Salir", message="¿Desear salir de la aplicación?")
if respuesta == "yes":
raiz.destroy()
def cerrar_documento():
messagebox.askretrycancel(
title="Reintentar",
message="No es posible cerrar. Documento bloqueado.")
def abrir_archivo():
fichero = filedialog.askopenfilename(
title="Abrir archivo",
initialdir="/",
filetypes=(("Ficheros de Excel", "*.xlsx"),
("Ficheros de texto", "*.txt"),
("Todos los archivos", "*.*")))
print(fichero)
raiz = Tk()
raiz.title("Menús")
raiz.iconbitmap("icon.ico")
barra_menu = Menu(raiz)
raiz.config(menu=barra_menu, width=300, height=300)
archivo_menu = Menu(barra_menu, tearoff=0)
archivo_menu.add_command(label="Nuevo")
archivo_menu.add_command(label="Abrir", command=abrir_archivo)
archivo_menu.add_separator()
archivo_menu.add_command(label="Guardar")
archivo_menu.add_command(label="Guardar como...")
archivo_menu.add_separator()
archivo_menu.add_command(label="Cerrar", command=cerrar_documento)
archivo_menu.add_command(label="Salir", command=salir_aplicacion)
edicion_menu = Menu(barra_menu, tearoff=0)
edicion_menu.add_command(label="Copiar")
edicion_menu.add_command(label="Cortar")
edicion_menu.add_command(label="Pegar")
herramientas_menu = Menu(barra_menu, tearoff=0)
ayuda_menu = Menu(barra_menu, tearoff=0)
ayuda_menu.add_command(label="Licencia", command=aviso_licencia)
ayuda_menu.add_separator()
ayuda_menu.add_command(label="Acerca de...", command=info_adicional)
barra_menu.add_cascade(label="Archivo", menu=archivo_menu)
barra_menu.add_cascade(label="Edición", menu=edicion_menu)
barra_menu.add_cascade(label="Herramientas", menu=herramientas_menu)
barra_menu.add_cascade(label="Ayuda", menu=ayuda_menu)
raiz.mainloop()

Infinitos Contrastes es una herramienta de aprendizaje en múltiples dimensiones. Su objetivo es posibilitar que el recorrido a través de las distintas enseñanzas se plantee de una manera activa, a partir de la transmisión de ideas o experiencias.
Mi educación e intereses condiciona evidentemente el trasfondo de esta página web, que refleja una constante batalla contra la Hidra de Lerna: cada conocimiento nuevo adquirido orgina, al menos, dos inesperadas carencias que suplir, haciendo de este infinito viaje una experiencia maravillosa.