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. Python.

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 agradecimiento a su autor.


El objetivo de esta entrada es triple: reconocer lo que ExcelMasPro nos aporta con su trabajo, mostrar otro modo de trabajar con 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 escrito copiando literalmente el que se expone en el vídeo, aunque el que aquí presento omite la formulación de la función y la llamada a la misma. Esto no altera el propio funcionamiento del código, aunque lo simplifica adaptándolo al modo de trabajar que se ajusta 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 (lo que Camelot identifica como tales, que no siempre lo son) y copiarlas 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; cosa que raramente he empleado yo por considerarlo innecesario en este nivel de aprendizaje, pero 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 (o de TKinter), incluyendo el procedimiento de crear y guardar datos en una hoja de cálculo de Excel. Supone poner a nuestra disposición una pequeña aplicación que nos resuelve el problema de acceder a cualquier documento pdf y extraer las tablas que contenga dejándonoslas disponibles como documento Excel. No siempre funciona perfectamente (sobre todo cuando los documentos son complejos) y no siempre extrae todos los datos de las tablas (interesa comprobar la calidad del resultado), pero al menos para pdf sencillo y tablas "normales" el éxito está garantizado.


martes, 10 de septiembre de 2024

Interfaz. Python.

Aplicaciones. Uso de variables y métodos


Avanzando en los conocimientos necesarios para crear aplicaciones viables utilizando TkInter, a los conocimientos necesarios sobre componentes y su posicionamientos, es necesario añadir el uso de variables de control y de los métodos para acceder al contenido de los componentes del formulario. Dedicaremos esta entrada a este fin.


Empezaré 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 entero 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 booleana 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 datos 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) 

NOTAObserva que, en este caso, el parámetro que empleamos en la definición de la etiqueta sTexto no es text sino textvariable.

  • trace() se emplea para "detectar" cuando una variable es leída, cambia de valor o es borrada. 

Dado que, por el momento no vamos a trabajar con el método trace(), no seguiré explicando aquí su uso. Dedicaremos el resto de esta entrada a ejemplificar con cierto detalle el uso de los anteriores (get() y set()) desarrollando un script de ejemplo.

miércoles, 22 de mayo de 2024

Interfaz. Python.

 TkInter. Segunda ventana

Mientras que 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 muy simple (mero ejercicio en realidad) de uso de dos ventanas, una principal y otra secundaria. Este es el código del script, desarrollado a partir de esta fuente:

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 ninguna 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

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

Interfaz. Python.

TkInter. Presentación

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 resulta mucho más funciona y 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 compensan el esfuerzo.


Utilizaré TkInter (tk) por estar incorporado en Python como conjunto de instrucciones y funciones que permiten implementar los widgets (componentes) de TkInter como clases de del lenguaje. Esto remite al concepto de POO y requiere el conocimiento de las funciones, pero iniciaremos el aprendizaje de TkInter obviando estos fundamentos para lograr un nivel básico de capacidad de uso de esta herramienta GUI. Incluso, a la inversa, es posible que este proceso nos sirva para aprender algo sobre el trabajo con objetos y el uso de funciones, pero no es este el objetivo y esos conceptos reciben atención específica en este mismo blog. 

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 que sirve de referencia.

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 utilizar TkInter 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 en realidad más de la culminación de un proceso que considero lógico y se concreta (mejor sería decir, 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.

Es obvio que lo que resta para alcanzar este segundo nivel es mucho, por lo que no tiene sentido tratar de concretar algo más cómo se podría plantear esta segunda fase. Ni tan siquiera es seguro que se alcance, ni no llegar a esa meta me supone ningún motivo de frustración, ya que este proceso de aprendizaje no pretende ser otra cosa que un divertimento personal. Si alguien tiene la prisa que yo no tenga, tendrá que buscarse otros medios.

Interfaz. Python.

TkInter. Etiquetas de texto

Una vez que hemos aprendido a crear la ventana, es posible empezar a incluir contenido en ella. Empezaremos por un componente sencillo de muy útil: una etiqueta.


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 el "programa" 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.

En esta entrada aprenderemos a trabajar con etiquetas de texto y en la siguiente incluiremos una etiqueta con imagen.

Obviamente, antes de utilizar una etiqueta debemos implementar la ventana en la que ésta residirá, así que empezaremos por implementar lo aprendido en la entrada anterior, referida a la creación de ventanas. No es mala práctica, ya que es lo 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. Paso a paso...

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 de 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 aprender. 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 la X
  • padx pady dar 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.

Interfaz. Python.

 TkInter. Entrada de texto (entry)

No es cierto que de poco nos pueden servir las etiquetas, pero sí que utilizar entrada incrementa en mucho el potencial de interactividad que puede tener una GUI. Además redefine ésta dentro de la fase input del algoritmo.


Implementar una entrada en una GUI basada en TkInter no es realmente un proceso complicado, aunque sí hay que tener claro que dentro de este tipo de GUI, una entrada tiene unas restricciones muy claras que impiden utilizar este componente para más que lo que su apariencia indica: introducir una única línea de texto y, si es posible, una única palabra, aunque no son tan estrechos sus límites.

Tras el consabido trabajo de generar una ventana y (es conveniente en este caso) también una etiqueta (así sabremos qué se nos pide escribir en la entrada, por ejemplo), deberemos seguir un procedimiento muy similar al seguido 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. Nada que no sepamos ya.

Cierto que no es esto todo lo que podemos hacer con la entrada (de hecho hasta el momento de poco serviría este componente), pero sí el 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, que será en breve.

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

Interfaz. Python.

 TkInter. Etiquetas con imagen

Además de texto (que es lo normal), las etiquetas también pueden contener imágenes.


Vimos en la entrada anterior cómo utilizar etiquetas para mostrar texto. En ésta aprenderemos a utilizarlas 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. Al igual que en los videos anteriores, sigo la explicación de Lilina Castañón (2017), añadiendo elementos y variaciones de mi cosecha. Pero en la explicación me voy a limitar a lo que es nuevo. El script al completo, como en ocasiones anteriores, lo podrás ver en 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 línea 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. Algunas de ellas pueden ser de interés, otras no tanto. 
etq1 = 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 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 y funcional y, en mi opinión, preferible.

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

Interfaz. Python.

TkInter. Botón de comando

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, recurso necesario para manejar comandos. Lo "malo" es que implementarlo nos obliga a entrar en contenidos  (mejor, enlazar con) que superan, con mucho, las pretensiones de esta incursión por el mundo de la GUI. Qué remedio.

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.

Tampoco hay que preocuparse demasiado por estas cuestiones: se hacen y en paz, pero con una advertencia: con esta entrada no iniciamos la segunda fase de este proceso; dar utilidad a la acción de la función sólo tiene, por ahora, una funcionalidad pedagógica (aprender a manejar botones)

Aun así, como hay que hacerla, hay que explicarla para que se entienda mejor el "proyecto": 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á... por ahora.

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.

Interfaz. Python.

TKInter. Ventana

La ventana constituye la base sobre la que se construye la GUI, así que es lógico empezar esta casa por la ventana😉. Un chiste malo, pero una buena decisión.


Dado que TkInter ya está instalado en Python, no es necesario instalarlo mediante PIP, pero no forma parte del paquete básico del lenguaje, por lo que es necesario importarlo. 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 (al principio del proceso de aprendizaje), 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") (que da título a la ventana)

ventana.geomtry('500x500') (que da tamaño en pilxels, horizontal x vertical)

ventana.configure (background="dark turquoise") (que establece un color de fondo) 

 Acceso al archivo

Interfaz. Python.

TkInter. Posicionamiento de componentes

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. No descarto tratar estos temas más adelante.


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 si se tratara de una cuadrícula (filas-columnas), lo que permite ubicar cada widget en la posición que se desee mediante un sencillo sistema de coordinadas. 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 el uso del método pack. Pero dedicaré más tiempo a explicar el uso de los otros dos métodos (grid y place) ya que hasta ahora no han sido empleados.

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 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 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 etq1etq1.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 coordinada 0,0.

En este archivo tienes el código creado empleando el método place(). Puedes probar 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ía interesarnos 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 del/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.