Buch lesen: «Desarrollo de interfaces gráficas en Python 3 con Tkinter»
Desarrollo de interfaces gráficas en Python 3 con Tkinter
Primera edición, 2022
© 2022 Tomás Domínguez Mínguez
© 2022 MARCOMBO, S. L.
Cubierta: ENEDENÚ DISEÑO GRÁFICO
Maquetación: cuantofalta.es
Correctores: Anna Alberola y Mónica Muñoz
Revisor técnico: Ferran Fàbregas
Directora de producción: M. Rosa Castillo
«Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a Cedro (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra».
ISBN: 978-84-267-3401-3
Producción del ePub: booqlab
A mi hijo
TABLA DE CONTENIDO
1. INTRODUCCIÓN
1.1 Qué es Tkinter
1.2 Su primera interfaz de usuario con Tkinter
2. LA VENTANA PRINCIPAL
3. POSICIONAMIENTO Y DISEÑO
3.1 El gestor de geometría pack
3.2 El gestor de geometría grid
3.3 El gestor de geometría place
4. OPCIONES COMUNES
4.1 Dimensiones
4.2 Color
4.3 Fuente
4.4 Foco
4.5 Posición
4.6 Relieve
4.7 Imágenes
4.8 Cursor del ratón
5. MÉTODOS COMUNES
5.1 Asignación y obtención de valores de atributos
5.2 Temporizadores
5.3 Gestión del foco
5.4 Manejo de dimensiones y posiciones
6. VARIABLES DE CONTROL
7. WIDGETS
7.1 Label y Message
7.1.1 Opciones y métodos
7.1.2 Práctica
7.2 Button
7.2.1 Opciones y métodos
7.2.2 Práctica
7.3 Entry
7.3.1 Opciones y métodos
7.3.2 Práctica
7.4 Spinbox
7.4.1 Opciones y métodos
7.4.2 Práctica
7.5 Listbox
7.5.1 Opciones y métodos
7.5.2 Práctica
7.6 Menús
7.6.1 Menú
7.6.2 OptionMenu
7.6.3 Menubutton
7.7 Cuadros de diálogo
7.7.1 colorchooser
7.7.2 messagebox
7.7.3 filedialog
7.8 Text
7.8.1 Opciones y métodos
7.8.2 Práctica
7.9 Scrollbar
7.9.1 Opciones y métodos
7.9.2 Práctica
7.10 Toplevel
7.10.1 Opciones y métodos
7.10.2 Práctica
7.11 Frame y LabelFrame
7.11.1 Opciones y métodos
7.11.2 Práctica
7.12 PanedWindow
7.12.1 Opciones y métodos
7.12.2 Práctica
7.13 Checkbutton y RadioButton
7.13.1 Opciones y métodos
7.13.2 Prácticas
7.14 Canvas
7.14.1 Opciones y métodos
7.14.2 Objetos gráficos
7.15 Scale
7.15.1 Opciones y métodos
7.15.2 Práctica
8. VALIDACIÓN DE ENTRADAS DE DATOS
9. EVENTOS
9.1 Vinculación de eventos con widgets
9.2 Secuencias de eventos
9.3 Controladores de eventos
9.4 Prácticas
9.4.1 Control del tamaño de la ventana
9.4.2 Atajos de teclado
9.4.3 Editor gráfico
10. EL MÓDULO TTK
10.1 Temas y estilos
10.2 Estado
10.3 Opciones comunes
10.4 Métodos comunes
10.5 Adaptación de una aplicación Tkinter a ttk
10.6 Widgets específicos de ttk
10.6.1 Combobox
10.6.2 Notebook
10.6.3 Progressbar
10.6.4 Separator
10.6.5 Sizegrip
11. ANEXO A. FUNDAMENTOS DE PYTHON
11.1 Entorno de desarrollo
11.1.1 Instalación
11.1.2 Descripción general
11.2 Sintaxis básica de Python
11.3 Variables
11.4 Tipos de datos básicos
11.4.1 Números
11.4.2 Cadenas de caracteres
11.4.3 Booleanos
11.4.4 Conversión de tipos
11.5 Operadores
11.6 Estructuras de control
11.6.1 if…else
11.6.2 while
11.6.3 for
11.7 Estructuras de datos
11.7.1 Listas
11.7.2 Tuplas
11.7.3 Conjuntos
11.7.4 Diccionarios
11.8 Entrada de datos de usuario
11.9 El depurador de código de Python
11.10 Funciones
11.11 Alcance de las variables
11.12 Clases y objetos
11.12.1 Herencia
11.13 Módulos
11.14 Threads
11.15 Ficheros
11.16 Excepciones
11.17 Práctica. Aplicación de gestión de clientes
12. ANEXO B. UNA ÚLTIMA PRÁCTICA
12.1 Módulo gestión de clientes
12.2 Interfaz de usuario
12.2.1 Funciones del menú “Archivo”
12.2.2 Funciones del menú “Operaciones”
12.2.3 Funciones del menú “Ayuda”
Unidad 1
INTRODUCCIÓN
Las interfaces de usuario son el medio de interacción entre las aplicaciones y las personas. Dependiendo de la forma en la que se lleve a cabo esta comunicación, se distinguen diferentes tipos: desde las de línea de comandos (en las que el intercambio de información se realiza utilizando únicamente texto), pasando por las interfaces gráficas, hasta las sofisticadas interfaces de voz (en las que el usuario ni siquiera necesita usar las manos).
Si conoce Python, sabrá que la forma más sencilla de intercambio de datos con un programa escrito en este lenguaje es mediante la shell de su entorno de desarrollo o la línea de comandos proporcionada por una ventana de símbolo del sistema en Windows, la aplicación Terminal en macOS o una consola de Linux (también llamada “terminal” o shell). Se trata, por lo tanto, de una interfaz textual.
Si no conoce Python, al final del libro dispone de un amplio anexo en el que se describen los fundamentos de este lenguaje. Allí se dan todos los conocimientos necesarios para poder seguir las prácticas propuestas.
Por sus características, este tipo de interfaces son muy limitadas, por lo que su utilización puede resultar difícil y engorrosa, aparte de poco atractiva, lo que puede provocar el rechazo de la aplicación. Por ese motivo, salvo casos excepcionales, la mayoría disponen de una interfaz gráfica. Los programas escritos en Python no iban a ser una excepción, y existen diversos paquetes para su desarrollo con los que podrá usar todo tipo de controles gráficos. Entre ellos, destacan:
•Tkinter. Librería estándar de Python. Viene integrada en el propio entorno.
•PyQT. Creada por Riverbank, proporciona un puente de acceso al conocido framework QT de desarrollo de interfaces gráficas de usuario. Incluye un entorno gráfico (PyQt5 designer) con el que incluso se podrían construir de forma sencilla, mediante el método de arrastrar y soltar (drag and drop) los controles gráficos. Se distribuye con una licencia GPL (GNU General Public License – Licencia Pública General de GNU) y otra comercial.
•PySide. Similar a la anterior, también proporciona un puente de acceso a QT; pero, a diferencia de PyQT, está disponible bajo la licencia LGPL (Lesser General Public License – Licencia Pública General Reducida de GNU). Surge como respuesta de Nokia (propietaria de QT) a la negativa de Riverbank de liberar PyQt bajo esta licencia, al requerir el pago de la comercial en caso de que la aplicación también lo sea (algo que no sucede con la licencia LGPL).
•Kivy. Se trata de un paquete de código abierto con licencia MIT (Massachusetts Institute of Technology – Instituto Tecnológico de Massachusetts), para el desarrollo rápido de interfaces gráficas multiplataforma. No solo funciona en Windows, Linux o macOS, sino también en Android e iOS. Los gráficos se procesan a través de OpenGL ES 2, en lugar de controles gráficos nativos, lo que hace que su apariencia sea similar, independientemente del sistema operativo en el que se ejecute.
El nombre del paquete QT juega con la forma en la que se pronuncia en inglés, similar a la palabra cute (“bonito” o “lindo”).
El término framework se puede traducir como “marco de trabajo”. En el contexto de QT, representa un entorno de desarrollo específico para interfaces de usuario.
OpenGL (Open Graphics Library) es un estándar que define una API multilenguaje y multiplataforma para el desarrollo de gráficos 2D y 3D.
Cada uno de estos paquetes tiene sus puntos fuertes, por lo que decidirse por uno u otro dependerá de las necesidades del proyecto en el que se vaya a emplear, la plataforma donde se ejecute y/o las preferencias del programador. De todos ellos, se ha elegido Tkinter, por ser el estándar de Python.
1.1 QUÉ ES TKINTER
Como se acaba de indicar, Tkinter es el paquete estándar de Python para el desarrollo de interfaces gráficas de usuario. Por ese motivo, se incluye al instalar el propio entorno, sin que sea necesario importar nada adicional para empezar a utilizarlo. Fue escrito por Fredrik Lundh y su nombre procede de la interfaz Tk en la que está basado. En realidad, se trata de una API (Application Programming Interface – interfaz de programación de aplicaciones) creada específicamente para utilizar Tk desde Python.
Tk es un framework multiplataforma de software libre, que proporciona una biblioteca de controles gráficos para el desarrollo de interfaces de usuario en Tcl. Al igual que Python, este sencillo lenguaje de propósito general es interpretado y soporta múltiples paradigmas de programación, incluyendo la orientada a objetos y la imperativa o tradicional.
Por lo tanto, Tkinter es un recubrimiento de Tcl que permite usar Tk desde Python, motivo por el que el intérprete de Tcl está incrustado en el de Python.
Tkinter ofrece una gran variedad de componente gráficos (a los que se conoce como widgets) con los que construir las interfaces de usuario. Para su diseño, se emplea una estructura jerárquica, en cuya parte superior está la ventana principal, donde se sitúan tanto los widgets sencillos (por ejemplo, botones, menús, campos de texto, etc.) como aquellos que a su vez contienen otros widgets (por ejemplo, frames, paneles, etc.).
Los widgets disponen de multitud de opciones con las que se determina el aspecto del componente gráfico (como, por ejemplo, el tamaño, el color, etc.), además de métodos que sirven para configurar dinámicamente dicha apariencia, situarlos en la posición que deban tener en pantalla, establecer su comportamiento, etc.
A lo largo del libro, no se describe la lista completa de atributos o métodos de todos los widgets. La documentación de esta librería se encuentra en https://docs.python.org/3/library/tkinter.html.
Cuando un usuario hace algo con alguno de estos controles gráficos (como, por ejemplo, pulsar un botón, seleccionar la opción de un menú, etc.), se genera un evento. Dichos eventos son capturados por la aplicación, lo cual provoca la ejecución de las funciones encargadas de responder de la forma deseada a cada uno de ellos. A estas funciones se las conoce como “controladores de eventos”, y son las que realmente permiten la interacción (intercambio de información) entre la aplicación y el usuario.
Algunos eventos también podrían dispararse por sucesos que no hayan sido causados por el usuario como, por ejemplo, un temporizador.
En el viaje que emprenda con la lectura de este libro, aprenderá a usar las clases que representan cada uno de los widgets que necesite, así como a gestionar los eventos que se produzcan cuando el usuario interactúe con ellos, creando interfaces gráficas atractivas y fáciles de manejar. Comencemos este recorrido con la más sencilla de todas.
1.2 SU PRIMERA INTERFAZ DE USUARIO CON TKINTER
Como no podía ser de otra manera, la primera interfaz de usuario que desarrollará será una que muestre el mensaje “¡Hola Mundo!” en una ventana. Su código es el siguiente:
En primer lugar, se importan las clases Tk y Label del paquete Tkinter. Con la primera se mostrará la ventana principal y, con la segunda, la etiqueta que contiene el texto “Hola Mundo”:
A continuación, se crea la instancia de la clase Tk, que representa la ventana principal (root). Una aplicación solo puede tener una ventana principal, a la que se la suele llamar “raíz”, porque es la que está en el nivel superior de la jerarquía de widgets; es decir, la que los agrupa a todos (en el siguiente apartado, se explicará en detalle este aspecto). En cualquier caso, siempre tendrá la posibilidad de crear otras ventanas independientes de la principal:
Después, se crea la etiqueta con el texto correspondiente:
Al texto se le ha añadido un retorno de carro y un espacio al principio y al final, para separarlo de los bordes de la ventana. Más adelante aprenderá cómo hacer esto mismo de una forma más adecuada.
La última sentencia sitúa la etiqueta en la ventana:
Ejecute este programa. Obtendrá el resultado que puede ver a continuación:
A título informativo, otra forma de programar una interfaz de usuario sería tratarla como un objeto. De esta forma, el código anterior también se podía haber escrito así:
Como puede observar, se declara la clase Interfaz, que representa a la interfaz de usuario de la aplicación. Su constructor será el encargado de situar la etiqueta en la ventana principal.
Las sentencias que siguen a la declaración de la clase anterior son las encargadas de crear la ventana principal (root) y, a continuación, la instancia de la clase que representa la interfaz (mi_interfaz).
El estilo de programación seguido en todas las prácticas de este libro será el primero. Se ha incluido este segundo estilo (deliberadamente simplificado) porque lo encontrará también con frecuencia en los foros y webs dedicados a Tkinter.
Unidad 2
LA VENTANA PRINCIPAL
Todos los widgets de una interfaz gráfica se organizan en una estructura jerárquica, cuyo nivel superior está ocupado por la ventana principal (main window en inglés), en la que se encuentran todos ellos. En Tkinter, esta se representa mediante la clase Tk, cuyo constructor es:
La forma más común de invocar este constructor es sin argumentos, aunque dispone de screenName, baseName, className y useTk.
La ventana principal contendrá tanto widgets elementales (por ejemplo, etiquetas, botones, etc.) como widgets contenedores (por ejemplo, frames, paneles, etc.). Estos últimos podrán incluir, a su vez, otros widgets básicos y/o contenedores, en cuyo caso se añadiría un nuevo nivel a la jerarquía.
La siguiente imagen muestra, de forma esquemática, una interfaz compuesta de una etiqueta y un frame que, a su vez, contiene otra etiqueta y un botón:
El diseño de esta interfaz se representa como una jerarquía de widgets con una estructura en árbol, tal como puede ver en la siguiente imagen:
Curiosamente, en informática las estructuras en árbol tienen la raíz en la parte superior y las ramas en la inferior (crece hacia abajo).
Solo puede haber una instancia de la clase Tk, es decir, una ventana principal. Si su aplicación requiriese varias ventanas, tendría que hacer uso de la clase Toplevel (se estudia más adelante).
El método principal de la clase Tk es:
Este método es el encargado de arrancar el bucle que atiende los eventos que se producen en la interfaz de la aplicación (como, por ejemplo, la pulsación de un botón, la selección de la opción de un menú, etc.).
El método mainloop() hace que la aplicación entre en un bucle infinito de espera y atención de eventos. Hasta que no se cierre la ventana principal, no se saldrá de él y, por lo tanto, no se ejecutará el código que hay a continuación de la sentencia que lo llama. Por ese motivo, se suele poner al final del programa. Si en un momento dado quisiera forzar la salida de este bucle, debería invocar el método quit().
Para poner un texto en la barra de título de la ventana, la clase Tk ofrece el método:
Si desea que la ventana tenga un tamaño determinado, use el método:
Tanto el ancho como el alto vendrán dados en píxeles; por ejemplo, para que una ventana tenga un tamaño de 200 píxeles de ancho y otros tantos de alto, el argumento de este método tendría el valor “200x200”.
Si no se indica un tamaño concreto, este se adapta al de los widgets que contiene.
Dicho tamaño podrá ser fijo e inmutable, o podrá ser modificado por el usuario a su gusto (comportamiento predeterminado). En este último caso, se podrá establecer que solo sea posible cambiar el ancho de la ventana, el alto o ambas dimensiones. La forma de hacerlo será invocando el método:
Ambos argumentos son de tipo booleano; uno permite modificar el ancho (primer argumento) y otro el alto (segundo argumento). Si su valor fuera True, podría hacerse; en caso contrario, no. Por lo tanto, si no quiere que el usuario redimensione la ventana, ambos argumentos deberán tomar el valor False.
Si se pudiera redimensionar la ventana, con los siguientes métodos se limitaría el tamaño mínimo y el máximo en cualquiera de sus dimensiones:
También en el caso de que sea posible cambiar el tamaño de la ventana, cuando la lógica del programa requiera saber sus dimensiones con objeto de ajustar su contenido, la forma de conocer el ancho y alto que tiene en un determinado momento sería mediante los métodos:
Para mostrar la ventana a pantalla completa, el método empleado sería:
Por el contrario, si lo que quiere es hacerla desaparecer de la pantalla (sin cerrarla), tendría que emplear alguno de estos métodos:
La diferencia entre ambos es que el primero hace desaparecer cualquier rastro de la ventana en la pantalla, mientras que el segundo la minimiza (se puede acceder a ella, por ejemplo, desde la barra de tareas de Windows).
Para volver a mostrarla, se usaría el método:
Cuando en la lógica de la aplicación fuera necesario conocer el estado de la ventana, el método empleado sería:
El valor devuelto por este método es:
•"normal ". La venta se muestra en pantalla de la forma habitual.
•"iconic". La ventana se muestra como un icono en la barra de tareas.
•"withdrawn". La ventana ha desaparecido de la pantalla, aunque no se ha cerrado.
•"zoomed". La ventana ocupa toda la pantalla.
Este método también se podría usar para establecer el estado de la ventana si se incluyera la opción newstate como argumento (sus valores serían los indicados anteriormente); por ejemplo, para maximizar la ventana de la aplicación, el método al que tendría que llamar de dicha ventana es:
Como habrá podido deducir, los métodos withdraw() e iconify() son equivalentes a state(newstate="withdrawn") y state(newstate="iconic"). Por la misma razón, deiconify() es equivalente a state(newstate="normal").
Si lo que quiere es cerrar la ventana (por ejemplo, cuando se ha pulsado un botón o la opción de salir de la aplicación), se llamaría al método:
De todos modos, la forma más común de cerrar una ventana es pulsando en el icono con forma de aspa que hay en su parte superior derecha. Para modificar ese comportamiento estándar (por ejemplo, avisando al usuario de que no se han guardado los cambios realizados en un fichero que se estaba editando), puede capturar el evento y realizar las tareas requeridas con el método:
En ese caso, en vez de cerrarse la ventana directamente, se ejecutaría la función cuyo nombre ha indicado en el segundo argumento.
El método protocol() se utiliza para la interacción entre la aplicación y el gestor de ventanas de su ordenador. Proporciona un mecanismo que intercepta eventos del sistema (primer argumento) para procesarlos de forma personalizada mediante una función, a la que se denomina “controlador de protocolo” (segundo argumento). De todos los posibles eventos que podrían capturarse, en los foros se indica que WM_SAVE_YOURSELF está obsoleto (evento que se lanza cuando la aplicación necesita guardar datos) y que tampoco es posible manejar correctamente WM_TAKE_FOCUS (la ventana coge el foco). Por lo tanto, solo se aconseja usar WM_DELETE_WINDOW.
A modo de ejemplo, el siguiente programa modifica su primera interfaz, con el fin de que la ventana principal tenga un tamaño inicial, que no podrá ser redimensionado a lo alto. Aunque sí podrá hacerlo a lo ancho; solo tendrá un margen entre 50 y 400 píxeles:
Como se ha comentado, el programa se basa en el desarrollado anteriormente (¡Hola Mundo!), por lo que solo se explicarán los cambios realizados sobre este. El primero es la incorporación de la sentencia que llama al método geometry() para establecer el tamaño inicial de la ventana en 200 × 50 píxeles:
A continuación, se invoca el método resizable(), cuyo segundo argumento tiene el valor False, lo que indica que la ventana no se puede redimensionar en el eje Y (a lo alto):
Las dos últimas sentencias que se han añadido establecen el ancho y el alto máximo y mínimo que puede tener la ventana principal. Puesto que con la sentencia anterior solo se permite redimensionarla a lo ancho, con estas limitará el tamaño entre 50 y 400 píxeles:
Ejecute el programa y pruebe que, efectivamente, el comportamiento de la ventana principal es el esperado.