Mostrando entradas con la etiqueta TkInter. Mostrar todas las entradas
Mostrando entradas con la etiqueta TkInter. Mostrar todas las entradas

miércoles, 24 de septiembre de 2025

Datos. Archivos pdf

Biblioteca Camelot (III)

Conversión de tablas a Excel

Para finalizar esta serie de entradas sobre Camelot, expongo en esta entrada la transcripción del script del autor de ExcelMasPro, cuyo vídeo cité como fuente en esta entrada. Sirva de reconocimiento y agradecimiento por su buen trabajo.

El objetivo de esta entrada es triple: reconocer lo que ExcelMasPro nos aporta con su trabajo, mostrar otro modo de trabajar Python y retomar la referencia a la relación entre este lenguaje y las hojas de cálculo. Y todo en el mismo paquete.

Paso sin más a presentar el script que he copiado literalmente del vídeo, aunque omito la formulación de la función y la llamada a la misma, cosa que altera el funcionamiento del código pero lo simplifica adaptándolo a mi nivel actual de conocimiento.



import camelot
import os
import pandas as pd
from tkinter import Tk
from tkinter.filedialog import askopenfilename, askdirectory
from datetime import datetime
#Ocultar ventana principal de TKinter
Tk().withdraw()

#Seleccionamos el archivo pdf
print("Seleccione el archivo PDF:")
pdf_file = askopenfilename(
	title = "Seleccionar el archivo PDF",
	filetypes=[("Archivos PDF","*.pdf")]
    )
if not pdf_file:
	print("No se seleccionó ningún archivo. Saliendo...")

#Seleccionar la carpeta de destino
print("Seleccione la carpeta donde desea guardar el archivo Excel:")
output_folder = askdirectory(title="Seleccionar carpeta de destino")
if not output_folder:
	print("No se seleccionó ninguna carpeta. Saliendo...")

#Nombrar el archivo Excel
now = datetime.now().strftime("%m-%d-%y-%H-%M")
output_excel = os.path.join(output_folder, f'Import_PDF_a_Excel_{now}.xlsx')

#Extracción de las tablas del pdf mediante Camelot
print("Extrayendo tablas del PDF...")
tables = camelot.read_pdf(pdf_file,pages="all", flavor="stream")

#Guardar las tablas en Excel
if not tables:
	print("No se encontraron tablas en el archivo PDF:")

#Guardando las tablas
with pd.ExcelWriter(output_excel, engine="openpyxl") as write:
	for i, table in enumerate(tables):
		table.df.to_excel(write, sheet_name = f'Tabla_{i+1}', index=False)


En resumen, lo que hace este script es acceder a un archivo pdf, capturar las tablas que incluye (mejor dicho, lo que Camelot identifica como tales, que no siempre lo son) y copia su contenido en una hoja de cálculo en la que crea tantas hojas como tablas encuentra.

El uso de TKinter permite al script facilitar una entrada de datos personalizada y en formato gráfico, algo que raramente he empleado por considerarlo innecesario en este nivel de aprendizaje, pero que en este caso me ha parecido interesante mantenerlo por mostrar otras formas de trabajar con Python (más cercanas al desarrollo de aplicaciones o DocAp).

Me ha parecido especialmente interesante el modo de construir el nombre del documento Excel creado, aunque he realizado un pequeño cambio en el formato (uniendo todos los elementos mediante guion bajo).

Realmente este script es más que una forma de mostrar el uso de Camelot, incluyendo el procedimiento de crear y guardar datos en una hoja de cálculo de Excel; supone además poner a nuestra disposición una pequeña aplicación que resuelve el problema de acceder a cualquier documento pdf y extraer las tablas que contenga dejándonoslas disponibles como hoja de cálculo. Por desgracia no siempre funciona correctamente (sobre todo cuando los documentos son complejos) y no siempre extrae todos los datos de las tablas (por lo que siempre será necesario comprobar el resultado), pero al menos para pdf sencillo y tablas "normales" el éxito sí está garantizado.

martes, 10 de septiembre de 2024

INTERFACES. Python.

Variables y métodos

Interfaces con Python

Dedicaremos esta entrada a conocer cómo hacer uso de variables y de los métodos de acceso a los controles de un formulario creado con TkInter.

Empezaremos por las variables de control, que son objetos (no sólo variables en sentido estricto) que se asocian a los componentes o widgets para almacenar sus valores y facilitar que éstos estén disponibles para el programa. Al igual que las variables, pueden ser de diferente tipo y se declaran de forma específica, siendo posible atribuirles datos o contenido en el momento en que se declaran:

  • nEntero = IntVar(value=1) -> Objeto variable integer con valor 1
  • nDecimal= DoubleVar(value=12,5) -> Objeto variable double con valor 12,5
  • tFrase=StringVar(value="Nombre del alumno") -> Objeto variable string con valor "Nombre del alumno"
  • bDatos=BooleanVar(value=True) -> Objeto variable boolean con valor Verdad (True)

El uso de estos objetos se combina con el de los métodos get(), set() y trace() que tienen respectivamente las siguientes funciones:

  • get() permite obtener el valor que contenga una variable de control y se emplea cuando es necesario conocer ese valor o dato
    • print ("Valor de la variable nEntero: ", nEntero.get())
  • set() asigna un valor o dato a una variable de control y se utiliza para modificar el valor o estado de un componente o widget
    • sNombre = StringVar()
    • sNombre.set("Python para principaiantes")
    • eTexto= tk.Label(ventana,textvariable=sNombre)
      • Observa que, en este caso, el parámetro que empleamos en la definición de la etiqueta sTexto no es text, es textvariable
    • trace()se emplea para "detectar" cuando una variable es leída, cambia de valor o es borrada.

miércoles, 22 de mayo de 2024

INTERFACES. Python.

TkInter. Segunda ventana

Interfaces con Python

En la entrada anterior vimos cómo trabajar con dos frames diferentes dentro de una misma ventana. En esta veremos cómo crear ventanas secundarias. Para simplificar el procedimiento prescindiremos de las frames y trabajaremos directamente sobre la ventana.

Crear una ventana secundaria puede ser una forma de dividir la presentación de la información en una aplicación de escritorio: mientras que la ventana principal contiene la GUI principal con los controles de introducción de datos, la ventana secundaria sirve para exponer el resultado de procesamiento realizado con esos datos, esto es, cumple funciones de output.

Veremos a continuación un ejemplo (mero ejercicio) de uso de dos ventanas, una principal y otra secundaria.


from tkinter import *

'''
Esta función crea una nueva ventana (secundaria), sitúa en ella una etiqueta
a la que manda el contenido de un campo de texto situado en la ventana principal.
Esta forma es válida para aplicaciones de escritorio.
'''
def envia_boton():
   ventana2 = Toplevel() # Crea la ventana secundaria
   ventana2.geometry("400x200")#dimensión
   ventana2.title("Ventana secundaria")#y título
   valor_entrada = entrada.get() #Entrada de texto desde ventana principal
   etiqueta = Label(ventana2, text="El valor introducido en la ventana principal es: " + valor_entrada).grid(row=0)
   cerrarSecundaria = Button(ventana2, text="Cerrar la ventana secundaria", command=ventana2.destroy).grid(row=3)#Cierre de ventana secundaria

root = Tk() #Creamos la ventana principal
root.title("Ventana principal")# título
root.geometry("300x100")#y dimensión

entrada = Entry(root, width=35) # campo de texto
entrada.grid(row=0)

envia = Button(root, text="Enviar", command=envia_boton).grid(row=1)#Comando de paso de contenido a ventana secundaria
cerrarPrincipal= Button(root, text="Cerrar ventana principal", command=root.destroy).grid(row=3)#Comando de cierre de la ventana principal

mainloop()

En este caso he optado por crear la ventana secundaria mediante una función que se ejecuta utilizando el comando envia. La ventana secundaria es generada por medio de la función Toplevel(), perteneciente a la librería TkInter (1).

El resto del código es muy simple y su comprensión no presenta dificultad. Además los comentarios incluidos en el código (excesivos de no ser por el carácter didáctico del propio script) permiten puntualizar cada uno de los componentes de este script, el cual puedes descargar desde este enlace.

NOTA< br>(1) También se puede identificar como tk.Toplevel(), ya que se trata de una clase de TkInter.

INTERFACES. Python.

TkInter. Presentación

Interfaces con Python

Claro que se puede prescindir de una interfaz gráfica (GUI) para crear aplicaciones: sólo tenemos que trabajar mediante órdenes desde la consola. Pero hacerlo mediante una GUI puede resultar más funcional y, sobre todo, más acorde con lo que se espera de una aplicación "moderna". Esto lo logramos interponiendo un conjunto de gráficos, lo que necesariamente complica el algoritmo, pero los beneficios, a veces, compensan el esfuerzo.

De entre las diferentes opciones disponibles en Python empezaremos a tratar el tema de la creación de interfaces en el entorno TkInter (tk) por estar incorporado en el lenguaje como conjunto de instrucciones y funciones que permiten implementar los widgets (componentes) de como clases.

Antes de continuar concretando en qué se va a basar la parte práctica de este enfoque, los siguientes enlaces remiten a la documentación básica.

Por lo que se refiere a este mi proceso de aprendizaje, me lo planteo en dos fases. En la primera, que es la que se inicia ahora, pretendo adquirir los conocimientos suficientes y necesarios para utilizarTkInter como herramienta para crear sencillas aplicaciones, dando una primera utilidad al aprendizaje de los rudimentos de Python. Para ello seguiré el siguiente recorrido en el que diferencio, a su vez, dos etapas:

  • Aprender de forma sistemática a implementar los componentes básicos de TkInter.
  • Y ser capaz de crear sencillas aplicaciones de escritorio con los componentes estudiados.

Como puedes apreciar, la segunda de estas fases es algo así como la puesta en práctica de la primera, por lo que no se trata tanto de crear utilidades reales como de llevar a la práctica lo aprendido a nivel "teórico".

La segunda fase no tiene, en realidad, fecha de inicio, ni está definida en términos de objetivos y fases como sí lo está la primera. Se trata de la culminación de un proceso que considero lógico y se concretará previsiblemente como aprendizaje sistemático y formal de TkInter como herramienta para la creación de GUI dentro de aplicaciones planteadas y diseñadas como herramientas funcionales para el desarrollo de la actividad profesional del SEO.

INTERFACES. Python.

Etiqueta de texto

Interfaces con Python

Una vez que hemos aprendido a crear la ventana, podemos empezar a incluir elementos en ella. Empezaremos por un componente sencillo y muy útil: una etiqueta o label.

Las etiquetas tienen una utilidad evidente: sirven para aportar información, bien como recurso para facilitar el input, bien para informar de los resultados obtenidos como consecuencia de procesamiento.

En el primer caso estamos pensando en TkInter como herramienta para la fase de entrada de información (input) y en el segundo entendemos que TkInter nos proporciona un medio para el output. En cualquiera de los dos, las etiquetas aportan la información necesaria para facilitar la comunicación entre la aplicación y el usuario.

Normalmente esa información es textual, pero también puede ser gráfica, así que necesitamos aprender a utilizar las etiquetas también como soporte para mostrar imágenes en la GUI, pero en esta entrada nos limitaresmos a aprenderemos a trabajar con etiquetas de texto, dejando el uso de imágenes para la siguiente.

Obviamente antes de utilizar una etiqueta debemos implementar la ventana en la que ésta residirá, así que empezaremos por aplicar lo aprendido en la entrada anterior, referida a la creación de ventanas. No es mala práctica, ya que es lo que deberemos hacer cada vez que aprendamos a utilizar un componente de TkInter.


import tkinter as tk

ventana = tk.Tk()
ventana.title("Primera ventana tKinter")
ventana.geometry('500x500')
ventana.configure(background="dark turquoise")

ventana.mainloop()

Deberemos situar el código que sigue antes de la última línea del anterior, pero esa es tarea para la puesta en práctica de esta "lección", por lo que queda de tu cuenta. Yo me limitaré ahora a explicar cómo implementar la etiqueta, que es lo que corresponde.

  1. Declaro la etiqueta (la nombro) y la identifico como elemento TkInter
    • etq = tk.Label(ventana, text = "Hola Mundo", bg="red",fg="white")
    • Observa cómo se escribe Label y cómo se asocia como componente de TkInter (tk.Label).
    • El contenido del paréntesis que sigue es muy importante ya que...
      • Asocia la etiqueta con la ventana
      • Establece el contenido de la etiqueta (text = ... )
      • Y da forma: el color de fondo de la etiqueta (bg) y el color del texto (fg)
  2. Después posiciono la etiqueta en la ventana.
    • etq.pack()
    • Esta sería la forma más simple y una de las formas de posicionar la etiqueta dentro de la ventana, pero no la única, como tendremos ocasión de ver. Además podemos aportar información que ayuda a controlar mejor y más en detalle el posicionamiento de la etiqueta. Para ello, dentro del paréntesis incluiremos lo siguiente:
      • etq.pack(fill=tk.X,padx=20,pady=10,ipadx=5,ipady=5,side=tk.LEFT)
    • fill expande la etiqueta en el eje de las X
    • padx y pady establece un margen alrededor de la etiqueta en ambas coordenadas
    • ipadx e ipady dan un margen alrededor del texto de la etiqueta, lo que implica también que dentro de ella.
  3. Una alternativa muy útil cuando la GUI es suficientemente compleja, es sustituir la escritura directa del texto en la etiqueta (ver 1) por el uso de una variable. en ese caso:
    • Primero debemos escribir la variable, cosa que, como sabemos, en Python se hace directamente:
      • sTexto = "La Casa Por El Tejado" -> Declara, tipifica y da contenido a la variable sTexto
    • Y sustituimos el texto de text por el nombre de la variable
      • etq = tk.Label(ventana, text = sTexto, bg="red",fg="white")

Aquí te dejo enlace al archivo .py con las alternativas de uso de la etiqueta para que analices los posibles usos de ambas.

INTERFACES. Python.

Entrada de texto

Interfaces con Python

Implementar una entrada (entry en una GUI basada en TkInter no es complicado, aunque sí hay que saber que este control tiene ciertas restricciones que impiden utilizarlo para más que lo que su apariencia indica: introducir una única línea de texto y preferiblemente una única palabra.

Tras el consabido trabajo de generar la ventana y (es conveniente en este caso) una etiqueta (así sabremos qué se nos pide escribir en la entrada), deberemos seguir un procedimiento muy similar al empleado para crear una etiqueta.


entrada = tk.Entry(ventana)
entrada.pack(fill=tk.X,padx=5,pady=5,ipadx=5,ipady=5)

La primera línea crea la entrada y la asocia a la ventana y la segunda la muestra y la posiciona.

Cierto que no es esto todo lo que podemos hacer con la entrada (de ser así de poco serviría este componente), pero sí es lo suficiente para alcanzar el objetivo de esta entrada. Otras cuestiones como, por ejemplo, qué hacer con el texto que escriba el usuario en ella, serán abordadas cuando corresponda.

Por el momento, te dejo acceso al script, más que nada para que veas cómo funciona como conjunto.

INTERFACES. Python.

TkInter. Etiquetas con imágenes

Interfaces con Python

Ya vimos cómo utilizar etiquetas para mostrar texto. Ahora aprenderemos a utilizar una etiqueta para mostrar imágenes. La función de esta opción es fundamentalmente estética (mejorar la apariencia de la GUI), pero también puede tener utilidad si las imágenes permiten ilustrar los conceptos que se trabajan en la propia GUI.

Lo primero que hay que saber es que existe una limitación de base: la imagen debe presentarse en formato .gif para ser mostrada en la etiqueta, además de estar ubicada en el mismo directorio en el que ubiquemos el script.

El resto (el código y su explicación) te lo muestro a continuación. Sigo aquí la explicación de Lilina Castañón (2017), añadiendo elementos y variaciones de mi cosecha. El script completo te queda accesible desde el enlace correspondiente al final de la entrada.

Primer paso: la primera novedad consiste en declarar la variable a asociar a la imagen:

  • img = tk.PhotoImage(file="imgtk.gif")
  • img =img.subsample(2,2)

La primera instrucción asocia la variable img con la función (el método) tk.PhotImage(), que tiene file como atributo. Observa que el nombre de la imagen va entre comillas.

Se plantea aquí una primera cuestión: la identificación del archivo de la imagen indica que se encuentra en el mismo directorio que el script, pero ¿sería posible ubicarlo en un subdirectorio dentro de éste?

Para ello creo dicho subdirectorio y modifico la primera línea de código...

  • img = tk.PhotoImage(file="img/imgtk.gif")

... y obtengo un resultado satisfactorio, luego es posible redireccionar la búsqueda del archivo img.gif a la posición deseada, pero habrá que tenerlo en cuenta a la hora de trasladar los materiales a un potencial usuario de los mismos. Para lo que interesa en este análisis, utilizaré la segunda formulación a fin de hacer más eficiente la gestión de recursos.

La segunda línea...

  • img =img.subsample(2,2)

... tiene dos particularidades: se trata del uso de un segundo método asociado directamente a la gestión o trabajo con archivos de imagen img.subsample() y tiene como objetivo establecer un tamaño de imagen en sus dos dimensiones (ancho y alto) y en términos de proporción: (1,1) significa tamaño real, (2,2) significa la mitad del tamaño y así sucesivamente.

Segundo paso (después de crear la ventana, evidentemente). Consiste en declarar la etiqueta y asignarle la imagen como contenido:

  • etq1 = tk.Label(image=img)

Esta es la forma básica, pero caben otras posibles que modifican la presentación que deriva de la anterior formulación. Algunas de ellas pueden ser de interés, otras no tanto.

  • tq1 = tk.Label(ventana,image=img)
  • etq1 = tk.Label(ventana,image=img,bg="red")
  • etq1 = tk.Label(ventana,image=img,text="Hola",bg="red")

Todas estas formulaciones añaden la referencia a la ventana, que curiosamente parece innecesaria cuando resulta fundamental en caso de etiquetas de texto.

La segunda de estas tres formulaciones incluye el atributo color de fondo (bg), que es admisible y funcional, por lo que lo recomiendo. La tercera incluye contenido textual, pero, aunque no da error, tampoco surte ningún efecto, predominando absolutamente la imagen, por lo que resulta superfluo.

Por mi parte, recomiendo el uso de la segunda de estas alternativas:

  • etq1 = tk.Label(ventana,image=img,bg="red")

Tercer paso. Consiste en asociar la etiqueta a la GUI mediante la fórmula etq1.pack(), la más simple, pero no la única, ya que también es posible (y creo que deseable) añadir referencias de tamaño y posicionamiento, como en esta segunda formulación...

  • etq1.pack(padx=30,pady=30,ipadx=10,ipady=10)

... que es válida, funcional y preferible por más completa.

Para finalizar, aquí accedes al script trabajado en esta entrada.

INTERFACES. Python.

TkInter. Botón de comando

Interfaces con Python

Para disponer de un mínimo de elementos que nos permitan crear una GUI funcional, a lo ya aprendido le falta algo indispensable: un botón de comando, recurso necesario para manejar eventos. Lo "malo" es que implementarlo nos obliga a entrar en contenidos (mejor, enlazar con) que supera las pretensiones de esta incursión

Además de estos condicionantes, explicar cómo implementar un botón conlleva hacer un primer acercamiento a alguna utilidad, puesto que no tiene mucho sentido no asociar el botón a una función que realice una acción y, por ello, que esa acción implique algún tipo de utilidad, por mínima que ésta sea.

En esta ocasión vamos a crear una calculadora de sumas muy sencillita. Contendrá cuatro etiquetas, dos entradas y dos botones de comando. Obviamente requiere la creación de dos funciones y el uso de una variable.

Como en otros casos, me voy a detener en explicar únicamente la implementación de lo que resulta novedoso, quedando el resto disponible en el script. Incluyo en el concepto "novedad" lo que no debería serlo: la creación de una función sencilla, aunque remito a las entradas ya publicadas con anterioridad que hablan de las funciones.

No obstante, no se entendería el código si no se presenta en su conjunto, así que habrás de hacer un ejercicio de repaso para entender lo que ya se explicó en entradas anteriores.

Para comprenderlo mejor, te puede ayudar esta breve descripción de las partes del código, que, por otro lado, quedan explicadas también en los comentarios del algoritmo:

Fases:

  1. Creamos las funciones necesarias: Suma() y Cerrar()
  2. Creamos la ventana
  3. Implementamos etiqueta descriptiva
  4. Implementamos los dos juegos de etiqueta + entrada con función de input
  5. Introducimos el primer botón de comando (función Sumar())
  6. Y a continuación la etiqueta en la que escribir el resultado de la suma
  7. Para finalizar con un botón de cierre de la ventana, asociado a la función Cerrar()

Recuerda que las funciones se escriben al inicio del script para que estén disponibles cuando sean llamadas desde los botones. Además, estas funciones requieren también del uso de al menos una variable: la necesaria para asociar el resultado al contenido (lo escrito) en la etiqueta ubicada después del primer botón.

Puedes acceder a un visionado más cómodo de la imagen haciendo clic en ella y regresar a la entrada pulsado la tecla escape o cerrando la ventana desde el  icono (X) situado en la esquina superior derecha.

En esta captura de pantalla, en la que hemos quitado los comentarios para facilitar el manejo de la imagen, puedes observar la posición que ocupan las dos funciones (líneas 2 a 6) y la que ocupa la variable sResultado (línea 11) que sirve para contener el resultado de la operación, tal y como se calcula en la primera función (Sumar()).

Respecto a la función Sumar(): a la variable interna suma se asigna el resultado de la operación en la que sumamos los dos contenidos de las dos entradas:

  • int(sSuma1.get())+int(sSuma2.get())
  • Observa que primero convertimos a integer int(sSuma1.get()) mediante la función int(), los valores capturados de las entradas. Esto es necesario porque las Entry devuelven valores string, no integer.
  • Observa también que lo que hacemos es utilizar el método get() (sSuma1.get()) como forma de acceder al contenido de las entradas.
    • Esa misma variable suma es la que sirve para retornar el resultado de la operación, asociado a la variable creada a tal fin return sResultado.set(suma), utilizando el método set() (el opuesto a get())

La posición que ocupa la variable sResultado (línea 11) y la forma en que se declara sResultado=tk.StringVar() son importantes, igual que la forma en que se asocia a la etiqueta en la que se expone (línea 24) textvariable=sResultado y no text=sResultado, como sería de esperar.

No estoy aun en condiciones de poder explicar todo esto, pero sí de aportar algunos datos, incluyendo lo obvio: poner la variable en otra posición o cambiar las formulaciones produce errores.

Posiblemente la posición en que se ubica la variable permita atribuirle el contenido resultante de la ejecución de la función: si la variable se coloca antes que la función y antes de la creación de la ventana, se produce error.

La variable debe ser declarada como variable tipo string mediante el método tk específico tk.StringVar(), declararla simplemente como variable no permite obtener el resultado deseado, ni de la función ni de ubicación en la etiqueta correspondiente.

Finalmente, la fórmula textvariable=sResultado permite identificar que el contenido de esta etiqueta va a ser una variable y no texto literal, pero no alcanzo a entender por qué en este caso la fórmula de asignación del contenido de una variable no funciona y en otras ocasiones sí.

De hecho si incluyo la variable string etq1Texto y le doy un contenido etq1Texto = "Primer sumando" y después la uso en sustitución del texto a introducir directamente en la segunda etiqueta...

  • etqSum1 = tk.Label(ventana, text=etq1Texto,bg="red",fg="white")

... el resultado es positivo. Esto parece indicar que la restricción observada (que no se produce en este ejemplo) está asociada al uso de la variable sResultado dentro de la función Sumar(), posiblemente por que no se trata de una variable, sino de un objeto que hereda métodos de la clase TkInter tk.StringVar(), en concreto, el método set()

La función Cerrar() asociada al segundo botón de comando (btnCerrar), es meramente un adelanto de la funcionalidad que se le puede dar a una GUI TkInter. Interesa entender qué hace el método destroye(), perteneciente al objeto ventana, que es a su vez un objeto hijo que hereda del objeto padre que podemos identificar (intuitivamente) con TkInter. Todo esto nos acerca al paradigma POO, pero no es necesario ir más allá.

Para finalizar: aunque la imagen anterior contiene todo el código de este script, creo que es mejor que puedas acceder al original que sí contiene los comentarios necesarios para leer su contenido con más facilidad. Aquí te dejo el enlace.

INTERFACES. Python.

TkInter. Ventana

Interfaces con Python

Dado que TkInter ya está disponible desde Python, no es necesario instalarlo mediante PIP, pero sí es necesario importarlo en el script. Para ello, y teniendo en cuenta los objetivos básicos de este primer acercamiento a TkInter, me limitaré a utilizar la forma más sencilla:

  • import tkinter as tk

... que nos ahorra tener que escribir el nombre completo. El segundo paso consiste en crear la ventana, asociada a la clase TkInter

  • ventana = tk.Tk()

Para que no se nos olvide, mejor crear ahora el procedimiento que permite mantener activa la ventana hasta que decidamos cerrarla. Esta instrucción debe ir al final del script, pero es fácil olvidarla, así que mejor acostumbrarse a crearla en este momento.

  • ventana.mainloop()

A partir a ahora, y entre la primera orden y esta última, vamos a introducir unas cuantas instrucciones para dar forma a nuestra ventana:

  • ventana.title ("Primera ventana"). Da título a la ventana.
  • ventana.geomtry('500x500'). Da tamaño en pilxels, horizontal x vertical
  • ventana.configure (background="dark turquoise"). Establece un color de fondo

Para acceder al código descarga este archivo

INTERFACES. Python.

TkInter. Posicionamiento de componentes

Interfaces con Python

Aunque hasta ahora sólo hemos utilizado el gestor geometry pack, TkInter dispone de otras opciones para posicionar los componentes o widgets en la ventana. Dedicaremos esta entrada a explicar cuáles son esta opciones y cómo se emplean.

Como referencia para tratar este tema tomaré el blog Python Para Impacientes, concretamente el segundo bloque de contenidos del índice TkInter.

Es posible utilizar los tres modelos de posicionamiento simultáneamente, bien en las diferentes ventanas que conforma una aplicación, bien en los diferentes macrocomponentes que se definan en la ventana única y principal (en este segundo caso, sólo si se definen tales componentes o frames).

Los tres modelos o formas de posicionamiento son los siguientes:

  • pack, que ha sido el modelo utilizado en las entradas anteriores y que utiliza como referencia los cuatro lados de la ventana. A parte de la simplicidad de uso, tiene la ventaja de que los componentes se adaptan a los cambios en el tamaño de la ventana. Su principal inconveniente son las limitadas posibilidades que presenta de controlar la ubicación deseada.
  • grid trata la ventana como una cuadrícula (filas-columnas), lo que permite ubicar cada widget en la posición que se desee mediante un sencillo sistema de coordenadas. También es posible expandir las filas y/o columnas que ocupa cada componente. Tiene como principal limitación sus dificultades para adaptarse a los cambios de tamaño de la ventana, siendo recomendable cuando ésta no es redimensionable.
  • Finalmente place es el método más sencillo de implementar, pero el más complicado para "acertar" con la ubicación deseada. Utiliza como referencia el valor conocido de las dimensiones de la ventana y la referencia a la posición x,y (0,0) como esquina superior izquierda. Presenta también limitaciones para las ventanas redimensionables, por lo que se recomienda que no lo sean.

Aunque ya usamos profusamente el método pack, me ha parecido necesario ilustrar de forma sistemática los distintos atributos de que dispone, así que dedicaré unas líneas a tratar y ejemplificar de nuevo su uso. Pero dedicaré más tiempo a explicar el uso de los otros dos métodos, ya que hasta ahora no han sido tratados.

Empezaremos por el método pack.

Esta imagen reproduce el código del script en el que mostramos una etiqueta en la ventana que hemos posicionado mediante el método pack. Destacar lo siguiente:

  • Líneas 1 y 2. Dos formas de importar una librería. La primera es conocida; la segunda permite concretar los componentes a importar, en este caso la librería font que empleamos primero en la asignación de contenido a la variable fuente (línea 5) y después "hereda" la etiqueta etq1 (línea 14). Tendremos ocasión de tratar el tema de los formatos de importación de librerías en otra ocasión.
  • Formulación de la configuración de la etiqueta etq1 (líneas 10-15) y uso del método pack para su posicionamiento en la ventana (líneas 16-23). Se trata "desmenuzar" la identificación de atributos en un formato que facilita su visualización. Es una formulación alternativa a la lineal (digamos la clásica), por ejemplo, la de configuración de la etiqueta:
    • etq1=tk.Label(ventana,text=sTexto,bg="red",fg="white",font=fuente)
  • Configuración de los atributos de posicionamiento pack()
    • fill permite extender la etiqueta en el eje X, en el eje Y o en ambos (BOTH), como es el caso. Ofrece estas tres opciones: tk.X, tk.Y, tk.BOHT.
    • side permite referir el posicionamiento respecto a los laterales TOP, LEFT o RIGHT de la ventana. Se presentan también estas tres opciones: tk.TOP, tk.LEFT, tk.RIGHT.
    • expand es atributo booleano que admite los valores True vs.False. Como su nombre indica, permite o no expandir el componente respecto a la ventana según se haya indicado (restringido) en función del uso de fill y side.
    • Además contamos con los atributos que, como sabemos, dimensionan el espacio alrededor del componente (>padx, pady) y del propio componente (ipadx, ipady), en este caso (etiqueta) tomando como referencia su texto.

Este archivo contiene el código anterior te permitirá realizar los cambios que desees y observar sus implicaciones para el posicionamiento y la apariencia de etq1.

Aplico ahora el segundo método (grid) en este script...

... en el que reproduzco parte del típico formulario de recogida de datos, aquí limitado a dos etiquetas y dos entradas.

Puedes observar las similitudes respecto al método pack y las diferencias, que se centran en dos cuestiones:

  • Ahora la ventana no es modificable (línea 7)
  • Y el posicionamiento de todos los elementos (las dos etiquetas y las dos entradas de datos) se realiza mediante grid(), que consta de atributos similares a los empleados con pack() (padx, pady, ipadx, ipady), pero también con otros específicos: column y row, con los que se identifican los parámetros columna-fila que sirven para posicionar los elementos.

Cuando interesa que un componte ocupe más de una fila o de una columna utilizaremos los atributos columnspan y rowspan respectivamente; por ejemplo (1):

  • columnspan= 2 -> El componente ocupa la columna asignada con column más la siguiente.

En este archivo encontrarás el código que he creado para trabajar con el método grid().

Para utilizar el método place() trabajaré con el mismo modelo de formulario que en el anterior, aumentando la distancia entre los componentes para que se aprecie mejor la diferencia y la incidencia del posicionamiento establecido.

En este método, como puedes ver en la imagen del código...

... utilizamos los atributos x e y para el posicionamiento del componente y eliminamos las referencias a sus bordes externos e internos. Por ejemplo, el posicionamiento de la etiqueta etq1 etq1.place(x=10,y=30) indica que la esquina superior izquierda del componente se ubica en la coordenada 10,30, en unidad pixel y en relación a la ventana, siendo el extremos superior izquierdo de la misma la coordenada 0,0.

En este archivo tienes el código creado empleando el método place(). Prueba a modificar las posiciones de los cuatro componentes del formulario para aprender a manejar las referencias x,y.

NOTA
(1) Si deseáramos que la ventana fuera modificable ventana.rezisable(1,1) podríamos utilizar el componente sticky, que tiene como parámetros las iniciales de los puntos cardinales (N,S,E,W) y que sirve para anclar los componentes en función de los puntos cardinales activados. No obstante, este tratamiento requiere alguna modificación más del script, por lo que, en principio utilizaremos grid() con ventanas no modificables.

INTERFACES. Python.

TkInter. Spinbox

Interfaces con Python

Spinbox es similar al control Entry, pero limitado a contenido numérico e incorporando dos botones para incrementar o disminuir este contenido contenido.

La configuración de un spinbox, como la de otros controles TkInter, se desarrolla en dos fases: configuración y posicionamiento.

En la fase de configuración...

  • Identificaremos el objeto como perteneciente al conjunto de objetos TkInter...
    • elementos = tk.Spinbox()
  • ... e incluiremos los parámetros que describen y configuran el componente, el primero de ellos el contenedor al que pertenece (ventana)
    • elementos=tk.Spinbox(ventana)

Pero con esto no hemos configurado aun el spinbox, ya que necesitamos al menos tres parámetros básicos para definir su comportamiento: el valor de inicio, el de finalización y el de paso o incremento:


elemento=tk.Spinbox(ventana,
 from_=10, to=30
 increment=0.5)  #-> El incremento puede obviarse, asumiendo entonces un incremento 1+1

Si queremos evitar que el usuario modifique el contenido del spinbox (es posible hacerlo por la similitud de su comportamiento respecto a un entry), deberemos usar el parámetro state con valor readonly (sólo lectura)


elemento=tk.Spinbox(ventana,
 from_=10, to=30
 increment=0.5
 state='readonly')

Podemos asociar un objeto variable al componente (vg. textvariable=num_via) así como una función mediante command

  • command=nombre_de_funcion

Finalmente, para evitar errores de funcionamiento cuando el usuario trata de forzar el valor de base o el superior, utilizaremos la instrucción warp con valor True warp=True, con lo que provocamos que, en lugar de permanecer sin modificar el valor del límite, cuando se trate de forzar el mínimo, el contador se sitúe en el máximo y viceversa.

En resumen, la configuración de un Spinbox podría ser la siguiente:

  • viajeros=tk.Spinbox(ventana,from_=1,to=20,increment=0.5,state='readonly',wrap=True,textvariable=num_via)

El posicionamiento del componente se rige por las normas generales que, a su vez, están condicionadas por el modelo de posicionamiento que se elija. Por ejemplo, para .pack()

  • viajeros.pack(fill=tk.X,expand=True,padx=20, pady=5)

Este es el resultado visual que obtendremos...

... y este el archivo original.

INTERFACES. Python.

TkInter. Checkbutton

Interfaces con Python

También llamado checkbox, el Checkbutton es un componente de formulario que presenta dos opciones: activado vs. desactivado, lo que equivale a considerarlo como booleano. También puede presentar un estado intermedio no activado, que no equivale formalmente a desactivado, aunque sí funcionalmente.

Su uso en un formulario es similar al de los botones de opción, aunque se diferencia de éstos en que no va asociado a un grupo de opciones. Esto es, que cada opción es independiente y no excluye las restantes.

En la creación de un checkbutton, al igual que la del resto de componentes, se diferencian dos fases: configuración y posicionamiento. Dado que la segunda no aporta especificidad y ya la hemos visto en anteriores entrada, y teniendo en cuenta que le dedicamos una entrada específica, en ésta me limitaré a explicar cómo se desarrolla la fase de configuración.

En primer lugar identificamos el componente con un nombre y lo definimos como parte de TkInter y por su tipo.

  • idavuelta = tk.Checkbutton()

A continuación identificamos el contenedor al que va asociado, siguiendo (hasta el momento) el mismo procedimiento que para cualquier otro componente del formulario.

  • idavuelta = tk.Checkbutton(ventana)

Es a partir de este momento cuando empezamos a introducir atributos específicos del componente hasta finalizar su configuración...


idavuelta = tk.Checkbutton(ventana,

                        text='Ida y vuelta',

                        variable=ida_vue,

                        onvalue=True,

                        offvalue=False)

... aunque la más específica es la que otorga significado booleano a sus dos estados: onvalue y offvalue. Además es necesario asociar una variable al checkbutton para poder acceder al estado del componente, ya que su utilidad dentro de un formulario radica precisamente en eso, estando generalmente asociado a formulaciones en términos de opcionalidad (if), pero esta es ya otra historia que contaremos cuando corresponda.

De momento nos quedamos con este script para poder manipularlo y analizar su funcionamiento.

INTERFACES. Python.

TkInter. Radiobutton

Interfaces con Python

Cuando interesa facilitar la elección de una opción predefinida como excluyente y formando parte de un conjunto limitado de posibilidades, los botones de opción (Radiobutton) son la mejor opción.

Vamos a ver en esta entrada cómo se configura. En realidad, cómo se configuran, ya que cada opción necesaria (cada botón de radio) se debe entender como un componente.

Eliminando del script el código de posicionamiento, que no interesa comentar aquí, el que se precisa para configurar estos componentes (tres en este caso) es el siguiente:


clase1 = tk.Radiobutton(ventana,

                        text='Clase turista (t)',

                        variable=clase,

                        value='t')

clase2 = tk.Radiobutton(ventana,

                        text='Primera clase (p)',

                        variable=clase,

                        value='p')

clase3 = tk.Radiobutton(ventana,

                        text='Clase Vip (v)',

                        variable=clase,

                        value='v')

Observa que todos hacen referencia a la misma variable (clase) y que cuentan también con un atributo de valor (value='t', por ejemplo). Mientras que esa referencia a la variable da unidad al conjunto, el valor específico (y diferente para cada botón) permitirá el posterior tratamiento del resultado del uso del formulario.

Pero como en otras ocasiones, también aquí eso queda para una entrada posterior. De moment aquí tienes el script de uso de radiobutton para que lo analices.

INTERFACES. Python.

TkInter. Combobox

Interfaces con Python

Próximo a la entrada (Entry) en cuanto a objetivo y función, el Combobox es un componente que permite al usuario elegir uno de los elementos de la lista de opciones del widget.

En esta entrada veremos el modo básico de implementar Combobox en un formulario siguiendo los dos procedimientos básicos: configuración y posicionamiento, aunque este último sólo con un objetivo funcional, que no didáctico.

Esta es la apariencia del formulario resultante del script que sirve de base, pobre de solemnidad, pero suficiente para que nos entendamos...

Observa el parecido con un Entry, siendo de hecho, la única diferencia observable (y después comprobable también en cuanto funcionamiento) la presencia de una pequeña flecha orientada hacia abajo y posicionada en el extremo derecho de la caja.

Como seguramente ya sabes, haciendo clic en esta flechita se despliega una lista de opciones de las cuales el usuario selecciona una, no pudiendo introducir otros contenidos que los disponibles en esta lista. Este es el funcionamiento esperable que se logra del siguiente modo (1):


combo = ttk.Combobox(
    state="readonly",
    values=["Python", "C", "C++", "Java"])
combo.place(x=50, y=50)

  • Primero asociamos el componente con el tipo de objeto perteneciente a TkInter
    • combo = ttk.Combobox()
  • Después, ya dentro del paréntesis, establecemos que no es posible al usuario introducir datos propios, indicando que este componente es sólo de lectura (2):
    • state="readonly"
  • Finalmente establecemos la lista de opciones utilizando para ello una lista asociada al atributo valores (values) (3
    • values=["Python", "C", "C++", "Java"])

Nos falta aprender muchas cosas sobre el uso de combobox, especialmente cómo accedemos a su contenido, pero esto queda para una entrada posterior. Por ahora, si lo deseas, puedes jugar con el código de este script para aprender a crearlos. También te puede ser útil esta página para saber más sobre este widget.

NOTAS
(1) Nos olvidamos de la última línea ya que constituye el procedimiento para posicionar el componente.
(2) Este es el comportamiento esperado, pero si queremos que el usuario puede incluir opciones no previstas, podemos eliminar este atributo.
(3) Observa el uso de corchetes para delimitar la lista y de comas y comillas para delimitar cada elemento. La similitud de Combobox con Entry se concreta en este caso, con que los valores de la lista son siempre asimilados a texto (de ahí que vayan entrecomillados), por lo que así será tratado cualquier valor aparentemente numérico que utilicemos.