Tkinter - CUADROS DE DIÁLOGO Y MENSAJES
En el capítulo anterior vimos como crear una aplicación con interfaz gráfica sencilla en Tkinter. Esta aplicación consistía en un formulario que contiene un botón, y al presionar dicho botón se imprimía un texto en la consola de comandos.
Vamos a ver los cuadros de diálogo, que son unas ventanas emergentes utilizadas para notificar algo al usuario, y constituyen una salida mucho mas atractiva que la consola. Así podrás prescindir de la consola al ejecutar tu programa y tendrás la sensación de haber construido una verdadera aplicación.
Módulo tkinter.messagebox
Crea un archivo .py, escribe este código y ejecútalo:
from tkinter import *
def mostrar_mensaje():
print('MENSAJE AL USUARIO')
root = Tk()
Button(root, text='Mostrar Mensaje', command=mostrar_mensaje).pack()
root.mainloop()
El código es sencillo:
- Importamos la librería Tkinter:
from tkinter import *
- Definimos una función
mostrar_mensaje
, que imprime un texto en la salida estandar. - Creamos el formulario (el widget principal):
root = Tk()
- Creamos un botón que será hijo de
root
y hacemos que sea visible con.pack()
. El comando de este botón es la funciónmostrar_mensaje
, que se ejecutará al hacer click en el botón:Button(root, text='Mostrar Mensaje', command=mostrar_mensaje).pack()
- Iniciamos el bucle principal para que se ponga en marcha la interfaz gráfica de la aplicación:
root.mainloop()
Si ejecutas el programa verás lo siguiente:
En la imágen he hecho click 5 veces sobre el botón, así que aparece el mensaje 5 veces.
Ahora vamos a redefinir la función mostrar_mensaje
para que el mensaje no se muestre por la consola, sino en una ventana (cuadro de diálogo):
from tkinter import *
from tkinter.messagebox import showinfo # <--- Ojo!
def mostrar_mensaje():
showinfo('MENSAJE', 'MENSAJE AL USUARIO') # <--- Ojo!
root = Tk()
Button(root, text='Mostrar Mensaje', command=mostrar_mensaje).pack()
root.mainloop()
Aquí hemos añadido un import: from tkinter.messagebox import showinfo
La función showinfo
es la que va a mostrar el cuadro de diálogo informativo. El primer argumento es el título y el segundo el mensaje de la ventana. showinfo
recibe keyword-arguments: showinfo(title='MENSAJE', message='MENSAJE AL USUARIO')
.
Al ejecutar el programa y hacer click sobre el botón deberías ver lo siguiente:
Fíjate en que mientras se encuentre visible el mensaje, si haces click en el botón [Mostrar Mensaje] no aparecen nuevos mensajes. Esto se debe a que showinfo
bloquea el bucle root.mainloop()
, que no puede continuar hasta cerrar el mensaje. En otras palabras, mientras el mensaje showinfo
esté abierto el programa se encuentra en pausa.
Ahora vamos a configurar el programa cambiando el título, icono y dimensiones, y de paso vamos a ver un nuevo argumento que podemos pasar al método .pack
.
El código ahora se verá así:
from tkinter import *
from tkinter.messagebox import showinfo
def mostrar_mensaje():
showinfo('MENSAJE', 'MENSAJE AL USUARIO')
root = Tk()
# INICIO CONFIGURACIÓN ESTILO ROOT
root.title('Mi programa :)')
root.iconbitmap('globos.ico')
root.geometry('240x120')
# FIN CONFIGURACIÓN ESTILO ROOT
boton = Button(root, text='Mostrar Mensaje', command=mostrar_mensaje)
boton.pack(expand=True, fill='both')
# expand=True centra el botón
# fill='both' hace que ocupe todo el espacio disponible
root.mainloop()
He utilizado este icono: globos.ico
Si ejecutas el programa verás como el botón ocupa todo el espacio disponible del formulario. Además si redimensionas el formulario verás como el botón se adapta al nuevo tamaño. Esto es gracias al argumento fill="both"
del método boton.pack
. Ahora el programa se ve así:
Como la función mostrar_mensaje
solamente ejecuta una instrucción podemos incluir esta instrucción en una función lambda y que sea esta el comando del botón. Borraríamos la función mostrar_mensaje
y cambiaríamos la definición del botón:
boton = Button(root, text='Mostrar Mensaje', command=lambda:showinfo('MENSAJE', 'MENSAJE AL USUARIO'))
Tipos de tkinter.messagebox
El módulo tkinter.messagebox
expone tres funciones para desplegar mensajes / cuadros de díalogo:
showinfo
- Para mostrar información neutra o positiva al usuario.showerror
- Para notificar un error al usuario.showwarning
- Advertencia al usuario.
Las funciones showinfo
, showerror
y showwarning
hacen exactamente lo mismo, la unica deiferencia es el logotipo del mensaje.
Vamos a crear una aplicación para ver estas tres funciones en acción:
from tkinter import Tk, Button, BOTH
from tkinter.messagebox import showinfo, showerror, showwarning
root = Tk()
root.title('Cuadros de Diálogo')
root.iconbitmap('globos.ico')
root.geometry('240x120')
root.resizable(False, False)
btnInfo = Button(root, text='showinfo', command=lambda:showinfo(
'Ejemplo showinfo', 'Mostrando un cuadro de diálogo informativo.'
))
btnError = Button(root, text='showerror', command=lambda:showerror(
'Ejemplo showerror', 'Mostrando un cuadro de diálogo de error.'
))
btnWarning = Button(root, text='showwarning', command=lambda:showwarning(
'Ejemplo showwarning', 'Mostrando un cuadro de diálodo de alerta.'
))
btnInfo.pack(expand=True, fill=BOTH)
btnError.pack(expand=True, fill=BOTH)
btnWarning.pack(expand=True, fill=BOTH)
root.mainloop()
El programa se verá así:
Los mensajes correspondientes a los tres botones son los siguientes:
En vista de este código y de su resultado detengámonos para revisar los siguientes puntos:
from tkinter import Tk, Button, BOTH
- En su lugar podríamos haber importado todas las variables/objetos de tkinter:from tkinter import *
, pero he importado sólo aquellas que estamos utilizando para hacer hincapié en la variableBOTH
: El módulotkinter
define algunas variables comunes comoBOTH
,LEFT
,RIGHT
,TOP
,BOTTOM
...etc. El valor de estas variables es un string que corresponde al identificador de la variable en minúsculas. Por ejemplo,BOTH
es igual a"both"
, de manera que donde pusimosbtnInfo.pack(expand=True, fill=BOTH)
podríamos haber escritobtnInfo.pack(expand=True, fill="both")
.from tkinter.messagebox import showinfo, showerror, showwarning
- Podríamos haber importadofrom tkinter.messagebox import *
y hubiese funcionado exactamente igual.btnInfo.pack(expand=True, fill=BOTH)
- Conexpand=True
centramos los botones, y confill=BOTH
hacemos que los botones ocupen todo el espacio disponible tanto horizontal como verticalmente.Al configurar
root.iconbitmap("globos.ico")
, los mensajes mostrados también presentan este icono.Al configurar
root.resizable(False, False)
, además de no poder cambiar las dimensiones del formulario, el botón de maximizar (al lado del botón [X]) está deshabilitado.
Módulo pymsgbox
El módulo pymsgbox define tres funciones que crean tres ventanas predefinidas con Tkinter:
pymsgbox.alert
.- Muestra una alerta al usuario.pymsgbox.confirm
.- Muestra una ventana de confirmación al usuario.pymsgbox.prompt
.- Permite la entrada de datos por parte del usuario.
Puedes ver estas tres funciones en acción en el siguiente ejemplo (que es una modificación del programa anterior):
from tkinter import Tk, Button, TOP, BOTH
from pymsgbox import alert, confirm, prompt
root = Tk()
root.title('Cuadros de Diálogo')
root.iconbitmap('images/globos.ico')
root.geometry('240x120')
root.resizable(False, False)
btnAlert = Button(root, text='alert', command=lambda:alert(
title='Ejemplo alert', text='Mostrando una alerta (Observa que solo hay un botón [Aceptar])'
))
btnConfirm = Button(root, text='confirm', command=lambda:confirm(
title='Ejemplo confirm', text='Mostrando un confirm (Observa que hay un botón [Aceptar] y un botón [Cancelar])'
))
btnPrompt = Button(root, text='prompt', command=lambda:prompt(
title='Ejemplo prompt', text='Mostrando un prompt. Escribe lo que quieras...'
))
btnAlert.pack(expand=True, fill=BOTH)
btnConfirm.pack(expand=True, fill=BOTH)
btnPrompt.pack(expand=True, fill=BOTH)
root.mainloop()
Aquí tienes una ilustración que muestra las ventanas mostradas al pulsar cada uno de los botones del programa:
Al cerrarse, estas ventanas devuelven un resultado que puede utilizarse para controlar el flujo del programa:
pymsgbox.alert
.- Siempre devuelve'OK'
, tanto si se pulsa el botón Aceptar como si se pulsa el botón [X].pymsgbox.confirm
.- Devuelve'OK'
si se pulsa el botón Aceptar, y devuelve'Cancel'
si se pulsa el botón Cancelar o [X].pymsgbox.prompt
.- Devuelve la cadena introducida en el cuadro de texto si se pulsa el botón OK. Si se pulsa el botón Cancel devuelveNone
. Si se pulsa [X] no devuelve ningún valor.
Puedes comprobar este funcionamiento en el siguiente programa:
from tkinter import Tk, Button, TOP, BOTH
from pymsgbox import alert, confirm, prompt
root = Tk()
root.title('Cuadros de Diálogo')
root.iconbitmap('images/globos.ico')
root.geometry('240x120')
root.resizable(False, False)
# INICIO DEFINICION DE FUNCIONES >>
def _confirm():
value = confirm(title='Ejemplo confirm', text='Mostrando un confirm (Observa que hay un botón [Aceptar] y un botón [Cancelar])')
if value == 'OK':
print('Has pulsado el boton [Aceptar]')
else:
print('Has pulsado el boton [Cancelar] o [X]')
def _prompt():
value = prompt(title='Ejemplo prompt', text='Mostrando un prompt. Escribe lo que quieras...')
if value:
print(f'Has introducido el texto: {value}')
else:
print('No has introducido un texto o has pulsado el botón [Cancel] o [X]')
# << FIN DEFINICION DE FUNCIONES
btnAlert = Button(root, text='alert', command=lambda:alert(
title='Ejemplo alert', text='Mostrando una alerta (Observa que solo hay un botón [Aceptar])'
))
btnConfirm = Button(root, text='confirm', command=_confirm) # <--- Ojo!
btnPrompt = Button(root, text='prompt', command=_prompt) # <--- Ojo!
btnAlert.pack(expand=True, fill=BOTH)
btnConfirm.pack(expand=True, fill=BOTH)
btnPrompt.pack(expand=True, fill=BOTH)
root.mainloop()
Como alert
siempre devuelve 'OK'
no es interesante crear una función que maneje el valor devuelto, pero para confirm
y prompt
si resulta interesante definir funciones que hagan una cosa u otra según este valor.
Comprueba la salida por consola de este programa al pulsar los distintos botones de los cuadros de diálogo.
Tanto para pymsgbox.alert
como para pymsgbox.confirm
, al hacer click en el botón y mostrar el cuadro de diálogo, parece que este es bloqueante como en las funciones tkinter.messagebox
. Sin embargo, si haces click varias veces en los botones de root
mientras un cuadro de diálogo está abierto, una vez lo cierres verás como aparecen el resto secuencialmente (dependiendo del numero de veces que hayas pulsado los botones).
En el caso de pymsgbox.prompt
no se observa un comportamiento bloqueante.
RESUMEN
En este artículo hemos visto dos módulos:
tkinter.messagebox
pymsgbox
Las funciones showinfo
, showerror
y showwarning
del módulo tkinter.messagebox
, así como las funciones pymsgbox.alert
y pymsgbox.confirm
nos permiten desplegar un mensaje que aporte información al usuario.
Los mensajes del módulo tkinter.messagebox
bloquean el bucle Tk.mainloop
, es decir, mientras esté abierto un mensaje de este módulo no hay escucha de eventos.
Los cuadros de diálogo pymsgbox.alert
y pymsgbox.confirm
son bloqueantes, pero no detienen la escucha de eventos. Solamente retrasan la ejecución de las funciones manejadoras hasta que se cierre el cuadro de diálogo.
El módulo pymsgbox
dispone además de la función pymsgbox.prompt
. Esta función no es bloqueante y permite la introducción de datos por parte del usuario.
Las funciones del módulo pymsgbox
, a diferencia de las del módulo tkinter.messagebox
, devuelven un valor que puede utilizarse para condicionar el flujo del programa.
GG
¡Continúa aprendiendo! - Configurar Widgets en Tkinter