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

domingo, 25 de mayo de 2025

Interfaz. OOo Basic.

Escritura de datos en tabla


Mientras que [en esta entrada] planteamos cómo automatizar un documento basado en texto "plano", ahora nos fijaremos en cómo acceder a un campo de una tabla. Y como ya vimos [en otro momento] podemos considerar ambos tipos de documento como las tipologías básicas desde la perspectiva de la automatización documental.

En realidad lo que voy a plantear en esta entrada es doblemente inexacto, pero consideraremos que no lo es para una mejor didáctica de la comprensión del procedimiento: ni es la única forma de abordar el trabajo con tablas (también se puede hacer mediante instrucciones basadas en "lenguaje macro"), ni se puede considerar limitado al trabajo con ellas (también es posible usarlos para localizar texto dentro de texto preescrito). Me estoy refiriendo al procedimiento de escritura de datos en campos de tablas mediante marcadores.

Efectivamente, aunque existen otras opciones y aunque su utilidad no se limita al trabajo con tablas, lo cierto es que la combinación de OOo Basic y el uso de marcadores resulta ser, en mi opinión, el procedimiento más "limpio" y funcional para solucionar el reto que supone automatizar la escritura de documentos prescriptivos o que han devenido en tales y que se caracterizan por el uso de tablas. Es en estas donde el uso de marcadores se hace aun más necesario como medio de automatización de la escritura en posiciones concretas (campos de la tabla).

El procedimiento podría ser el siguiente:
  1. Acceder al documento portador de tablas.
  2. Establecer los marcadores en las celdas y/o en las posiciones del documento (Insertar | Marcador)
  3.  Crear el módulo para escribir el script (Herramientas | Macros | Organizar Macros | Basic).
  4. Desarrollar el script.
  5. Implementar un comando en el documento para facilitar el acceso al script 
Es importante insistir, aunque parezca obvio, en que ahora trabajamos sobre un documento con el que es muy posible que hayamos trabajado muchas veces sin macros, por lo que lo conocemos perfectamente y sabemos qué tipo de datos requiere, dónde se sitúa cada uno de ellos (y más si es un documento prescriptivo) y cuánto tiempo lleva cumplimentarlo.

Dado que lo urgente se impone siempre, es también muy probable que no hayamos pensando nunca (o muy pocas veces) en automatizarlo, pero hacerlo supone, en el medio plazo y a poco que lo usemos, un ahorro de tiempo considerable. Aunque para ello normalmente hace falta desarrollar un DocAp al completo, y no simplemente lo que ahora vamos a explicar.

Dim mDatos(4) As String, mMarcas() As String
Dim oMarca As Object
Dim i As Integer

mDatos(0) = InputBox("Fecha de la actuación:")
mDatos(1) = InputBox("Nombre del Servicio de Orientación:")
mDatos(2) = InputBox("Nombre del Orientador o de la Orientadora:")
mDatos(3) = InputBox("Nota:")

mMarcas() = Array ("m0","m1","m2","m3")

For i = 0 To UBound(mMarcas())
oMarca = ThisComponent.getBookmarks().getByName(mMarcas(i))
oMarca.getAnchor.setString(mDatos(i))
Next

Este script contiene dos matrices para facilitar el posterior desarrollo del script. El contenidos de ambas queda claro en el script.

Aunque en el planteamiento más simple no sería necesario, un script mínimamente realista los requiere. Gracias a ellas, al final del script es posible automatizar eficientemente la escritura de los datos en el documento mediante un bucle (For i = 0 To UBound(mMarcas())).

El uso de la función InputBox() es la herramienta que facilita la interacción con el usuario, la cual se basa en el comando inserto en el documento (Cumplimentar datos).

Es clave para el correcto funcionamiento del script el uso de la variable de objeto oMarca, que se asocia a las funciones que nos dan acceso a los objetos marcadores insertados en el documento en el punto 2 del procedimiento. Este proceso, que se desarrolla dentro del bucle comentado antes, lo hace en dos momentos:

  • Acceso al marcador (oMarca = ThisComponent.getBookmarks().getByName(mMarcas(i)))
  • Escritura de datos (oMarca.getAnchor.setString(mDatos(i)))
Obsérvese que en cada uno de ellos empleamos una de las matrices explicadas antes.

Es importante finalizar todo este trabajo creando dos documentos: uno en formato odt, como documento-base, y otro en formato plantilla (ott) que será el que utilicemos en la práctica. El documento-base queda como reserva para posibles modificaciones o mejoras, además de para a posible pérdida del documento plantilla.

Documentos. Desde este enlace puedes descargar la versión odt. Antes de hacer nada, te recomiendo que, una vez descargado, generes una copia como plantilla (Archivo | Guardar como).

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.

jueves, 13 de junio de 2024

Interfaz. OOo Basic.

Diálogo (e) Casilla de verificación y botón de opción (1).

Ambos controles presentan una apariencia similar, pero se diferencian en su forma de funcionamiento y también en el modo en que podemos acceder al resultado de su manipulación por el usuario. Veámoslo con más detalle sobre un ejemplo de uso de Diálogo sobre Writer, aunque ya sabes que sería igual si trabajáramos con un documento Calc, por lo que esta explicación sirve para trabajar sobre ambos servicios.



Por lo que conocemos de los controles de un Formulario, ya sabemos que la diferencia entre una casilla de verificación (CheckBox) y un botón de opción (OptionButton) es que en la primera podemos seleccionar todas las casillas presentes en el Dialogo, pero sólo uno de los n botones de que esté compuesto el conjunto de opciones. De ahí que su funcionalidad dentro de la interfaz sea diferente, tal y como expresan los términos que les acompaña.

Voy a ejemplificar el uso y acceso a Casilla y botones. sobre un docap-simple basado en Writer que, como en el [caso anterior] permite crear un listado simple, al que ahora añadiremos...
  • Mediante una casilla la especificación de si se trata de un alumno con PTI-ACS
  • Y mediante botones si se trata de un alumno con apoyos de AL, PT o ambos.
Las diferencias entre Casilla y Botón se observan más en la construcción de ambas y en las peculiaridades de cada una de ellas como control que en el modo de trabajar con ambas. El primer aspecto no se trata en esta entrada, aunque algo se ha dicho antes sobre el funcionamiento diferenciado de estos dos controles, pero sí expongo a continuación el modo de acceder al contenido seleccionado por el usuario. Dado que es igual para cada Botón o Casilla, lo que explico para uno de estos elementos se debe entender aplicable al resto.

Iniciamos con la declaración de tres variables, por ejemplo, 
  • Dim obtnPT As Object, para la asignación o referenciar el objeto Botón 
  • Dim bPT As Boolean, para contener el valor del objeto (2)
  • Dim sApoyo As String, para contener la implicación o resultado que deriva de dicho valor.
La declaración de las variables antecede a la apertura del diálogo, que en el ejemplo que desarrollo se concreta como llamada a la función  Abrir() que cuenta con un parámetro (nDlg), variable String en la que referenciar el nombre del Dialogo, tal y como se establece en la construcción de éste (Dialog1).

El segundo paso consiste en acceder al objeto Botón (o Casilla), lo que implica conocer el nombre del control, como sucede en el resto de los elementos del Diálogo. La función o método requerido para ello es getControl() y el parámetro requerido es el nombre del control ("btnPT")
  • obtnPT = oDialogo.getControl("btnPT")
Esta segunda fase se desarrolla tras la apertura algorítmica del Diálogo y antes de visibilizarlo (ejecutarlo), por lo que anteceden a la instrucción oDialogo.execute().

A continuación, tras ejecutar el Diálogo, asigno a la variable Boolean  bPT el valor (True/False) resultante de la acción del usuario sobre el control, y que está contenido en el atributo State propio de los objetos Botón y Casilla.
  • bPT = obtnPT.State
Este paso no es estrictamente necesario, ya que podemos utilizar directamente la expresión referenciada (obtnPT.State) en el proceso posterior, pero resulta de utilidad didáctica para entender mejor cómo se desarrolla el proceso. 

Finalmente hacemos uso de una estructura condicional (If) para explicitar lo que la elección del usuario implica cuando selecciona o no el control Botón. Aquí se impone la lógica del algoritmo, que es la que da sentido a la acción del usuario. Por ejemplo, en nuestro caso, si el usuario hace clic (selecciona) la opción que representa nuestro botón btnPT, lo que se expresa es que el alumno cuenta con apoyo de PT, luego, si hace esta elección, el State del control será True. En caso contrario será False. En función de esto, la variable que expresa en forma de texto esa implicación tendrá un valor u otro. Todo ello se expresa como sigue (3):

If bPT= True Then
sApoyo = "Apoyo especializado de PT"
ElseIf bAL = True Then
sApoyo = "Apoyo especializado de AL"
ElseIf bAmbos = True Then
sApoyo = "Apoyos especializados de PT y de AL"
Else
sApoyo = ""
End If

La pulsación de cada uno de los tres botones (activando a True su State) (4) se traduce en un valor (que se expresa como String) que se asigna a la variable de respuesta (sApoyo). En caso de no activarse ningún botónsApoyo recibe como valor una cadena vacía.

Documento. [Lista simple]

En el IDE de este documento puedes encontrar el código que incluye el acceso a los controles Botón y Casilla implementados en el diálogo que se activa mediante el comando Dialogo Lista.

NOTAS

(1) Esta entrada resulta de la la actualización y modificación de una anterior publicada el 4/07/2023, a la que sustituye.
(2) Como veremos a continuación, el valor, que depende de que el usuario seleccione (True) o no (False) el botón (o la casilla) es, en consecuencia un valor booleano. Como se explica en nota 4, estas variables no son imprescindibles, pero permiten explicar mejor el procedimiento, de ahí que haga uso de ellas.
(3) Utilizo la formulación completa del condicional, tal y como se emplea para dar cuenta del conjunto de botones, no sólo del control que centra la explicación. De este modo se comprende mejor el procedimiento. En esto hay diferencia respecto al control Casilla, ya que al ser cada una independiente del resto, es necesario utilizar una estructura condicional independiente para cada una de ellas. No obstante, esta diferencia, aunque coherente con la lógica del funcionamiento diferencial de ambos controles, es más formal que real, puesto que también es posible (aunque no conveniente) utilizar una estructura condicional para cada botón.
(4En el condicional If bPT= True Then se puede sustituir la variable bPT por la expresión que ésta referencia (obtnPT.State), con la que la expresión quedaría como sigue:  If obtnPT.State= True Then. De este modo se simplifica el algoritmo y se ahorra memoria RAM al eliminar la duplicidad espacio de memoria reservado para el mismo dato. No obstante prefiero (en este ejemplo y en función del carácter didáctico de esta la entrada) ser menos eficiente el el uso de RAM y más claro en la explicación del proceso. 

jueves, 30 de mayo de 2024

Intefaz. OOo Basic.

Diálogo (c) Cuadro de texto.

El Cuadro de texto (TxtFieldes el control más usado en Formulario y en Diálogo para la entrada de datos por parte del usuario; cumple en esto la misma función que InputBox() pero con mayores opciones de formato y de funcionalidad. Además también es interesante acceder al modelo de objeto para introducir contenido mediante un script.


Con Cuadro de texto (Texto en adelante, para simplificar) iniciamos el mismo recorrido por controles de Diálogo que realizamos en otro momento por los controles de Formulario. Lo hacemos también ahora por el control de uso más frecuente en la fase input. Además, aunque hay dos formas de uso, la más frecuente es la de permitir entrada de información por parte del usuario. Sobre ella y la posterior captura del texto introducido trataremos a continuación.

La implementación del control Texto se realiza manualmente desde el IDE, al igual que sucede con su contenedor Diálogo, por lo que remito a [esta entrada] para recordar cómo proceder. Además al contrario que en Formulario, ahora sí es necesario añadir una [Etiqueta] como elemento informativo que facilita al usuario la comprensión del contenido que se espera en Texto (1

Lo que sigue es válido para cualquier documento que incluya un Diálogo, con independencia del servicio desde el que trabajemos. Yo lo haré sobre un documento Writer, reproduciendo (con alguna modificación) lo que muestra la imagen que sigue.



Aunque los pasos previos (crear script de acceso y cierre del Diálogo) los doy por sabidos, ya que han sido trabajados con detalle [(en una entrada anterior]. Así que pasaré directamente al trabajo con los controles de Texto.

El acceso al contenido de los controles de texto se puede plantear de diferentes formas. Aquí mostraré dos de ellas (en Documentos podrás ver ambas versiones). En la primera el acceso se realiza desde el script asociado al cierre del formulario y en la segunda desde el script de manejo del contenido que, a su vez, se asocia con el de acceso al diálogo. La diferencia fundamental entre ambos es que en la primera necesitamos que las variables Object de acceso a los controles Texto se declaren como públicas (al inicio del módulo), mientras que en segundo son variables privadas del script de acceso al contenido de los controles. 

Veamos en detalle la primera formulación, que consta de cuatro script:

  • Uno para acceder al Diálogo (AbrirDialogo), que lleva el peso de acceder a los controles Texto
  • Dos para cerrarlo (CerrarDiálogo y Cerrar). El primero lleva la carga de generar el sistema de acceso  al contenidos de los controles y su paso a variables de tipo String. El segundo es una subrutina llamada desde el primero.
  • Y uno de escritura en el documento (Agenda), en realidad una subrutina de escritura llamada desde CerrarDialogo.
En esta formulación, aunque el peso del acceso a los controles se reparte entre AbrirDialogo y CerrarDialogo, es este último el que cumple la función principal, ya que contiene las variables String, captura en ellas el contenido de las variables Object y llama a las dos subrutinas auxiliares (Cerrar y Agenda)

La segunda formulación consta también de cuatro script, dos principales...

  • AbrirDiálogo, que nos permite acceder al diálogo y a sus controles, pero que a diferencia de la formulación anterior, gestiona también el acceso a los dos script auxiliares, así como el acceso al contenido de los controles de texto.
  • CerrarDialogo, que en esta versión tiene como única función lo que indica su nombre, perdiendo el protagonismo que tenía en la primera formulación.
... y dos secundarios, asociados, como dije antes, al script principal AbrirDialogo.

  • La subrutina Abrir(nDialog As Stirng), cuya función es facilitar el acceso al diálogo que se le pase como parámetro desde el script principal.
  • Y la subrutina Agenda(sTexto As String), cuya función es completar el proceso escribiendo en el documento el contenido textual creado en el script principal y pasado como parámetro.

Vistas ambas formas de acceso al texto contenido en los controles Texto, queda a tu decisión cual emplear. Personalmente prefiero la segunda por no hacer demasiado extensa la lista de variables públicas, por diferenciar el acceso al contenido del acceso al Diálogo y por diferenciar estos procesos anteriores  del de cierre del Diálogo. En mi opinión es un procedimiento más claro en cuanto a la lógica del algoritmo.

En cualquier caso, lo que queda claramente establecido es la secuencia de pasos a dar para acceder al contenido del control Texto:

  • Primero: Accedemos a la librería que contiene el objeto DiálogoDialogLibraries.LoadLibrary( "Standard")
  • Segundo: Accedemos al objeto Diálogo propiamente dicho: oDialogo = CreateUnoDialog(DialogLibraries.Standard.getByName(nDialog))

Estos dos pasos son previos al acceso al control y son comunes al acceso a cualquier control. En la primera versión estos dos pasos son asumidos por el script AbrirDialogo y en la segunda corre a cargo de la subrutina  Abrir(nDialog As Stirng) que es llamada desde el script AbrirDiálogo, una vez establecido el nombre del Diálogo al que queremos acceder.

  • Tercero: Accedemos a cada uno de los objetos control Texto que contiene el objeto contenedor DialogooTxtNombre = oDialogo.getControl("txtNombre")

En ambas formulaciones, este proceso se desarrolla sobre el script AbrirDialogo una vez se ha accedido a éste y no antes, ya que de invertir el orden no podríamos acceder a los controles en tanto que son objetos que pertenecen al objeto que los contiene (el Dialogo). Sin embargo este acceso a los objetos control Texto precede a la propia visualización del Diálogo.

  • Cuarto: Accedemos al contenidos de los controles Texto, lo que se concreta como acceso a su atributo .TextsNombre = oTxtNombre.Text. La asignación de este contenido a una variable de tipo String es optativo, aunque facilita el posterior y diferenciado trabajo con ese contenido.

Aquí es donde más difieren ambas formulaciones: mientras que en la primera es un proceso que se desarrolla en CerrarDialogo, en la segunda es asumido por AbrirDialogo. No difieren en el tratamiento posterior que se hace de ese contenido, ya que ambas formulaciones generan un contenido textual resultante de la concatenación de las variables String que pasan como parámetro a la subrutina de escritura Agenda()

Es interesante observar la posición que ocupa este proceso en el desarrollo del script AbrirDialogo en la segunda formulación (en la primera no se aprecia este detalle al activarse previamente el script de apertura: El acceso al contenido del atributo .Text del control Texto se realiza una ver que se visualiza el objeto contenedor Dialogo (oDialogo.execute())y no antes.

Hemos visto hasta este punto cómo acceder al contenido textual introducido por el usuario en el control Texto, con lo que estamos en disposición de afrontar los procedimientos de trabajo que son más frecuentes en la práctica; pero también es conveniente aprender a introducir contenido en estos controles mediante código. No es ésta una forma de trabajo especialmente frecuente, pero sí resulta de interés en determinadas aplicaciones. Además es sencillo de hacer: es suficiente con invertir la dirección de la asignación en el momento en que accedemos al atributo .Text desde una variable String:

  • Para asignar el contenido de Texto a una variable: sNombre = oTxtNombre.Text
  • Para asignar a un control Texto el contenido textual de una variable: oTxtNombre.Text = sNombre

No obstante sí hay una diferencia importante entre ambos procesos: cuando aplicamos el primero (sNombre = oTxtNombre.Text) lo hacemos después de visualizar el Diálogo (después de oDialogo.execute()); pero cuando aplicamos el segundo (oTxtNombre.Text = sNombre), dar contenido a la variable String y asignarla al objeto control Texto (a su atributo Text) debe hacerse antes de visualizar el objeto Diálogo.

Documentos:

NOTAS

(1) Se puede decir que ambos controles (Etiqueta y en este caso Texto) forman una unidad. Lo mismo podremos decir del resto de los controles que se explicarán en entradas sucesivas.

domingo, 26 de mayo de 2024

Interfaz. OOo Basic.

 Diálogo (b). Etiqueta

Aunque un Formulario también puede contener etiquetas, al estar creado sobre el documento, es fácil suplir su función por la mera escritura de su contenido sobre el propio documento. En el caso del Diálogo esto no es posible, así que no queda otro remedio que utilizarlas.


En el Diálogo una etiqueta puede cumplir varias funciones, según cómo se configure: puede servir como información sobre el control que sigue, pero también puede considerarse un elemento informativo sobre qué se solicita con el diálogo, sobre el proceso a seguir o sobre los resultados obtenidos. En todos estos (segundos) casos, la etiqueta es mucho más que un mero auxiliar de otro control, asemejándose a las funciones de información que son propias de MsgBox (1)

Generalmente la etiqueta es un control estático, lo que significa que el contenido que le demos en la fase de construcción del diálogo es que que va a mantener en todo momento. En este supuesto el uso de Etiqueta se limita a su implementación en la fase de creación del Diálogo, con lo que casi no deberíamos decir aquí nada más (2). Y de hecho podríamos funcionar con este planteamiento incluso aunque necesitemos aportar información diferente (3) a lo largo del script (4); en este caso, aunque no es precisamente ejemplo de ahorro de recursos y de simplicidad, es posible crear varios diálogos cada uno con su(s) etiquetas, y en cada una con su propio texto. Pero también podemos modificar el contenido o texto que mostrar; en este caso ya no podemos hablar de la etiqueta como elemento estático. Veremos ahora cómo.

Para que una etiqueta presente un contenido textual diferente necesitamos acceder a su modelo de objeto, lo cual nos permite modificar algunas de sus características, incluyendo (para lo que nos interesa ahora) el contenido textual de la propia etiqueta.

Dim oEtq As Object

Dim sEtqConten As String 

sEtqConten = "Este es el texto que mostrará la etiqueta" 

oEtq = oDlg.detControl()"etqInicio")

With oEtq.getModel

.Label = sEtqConten

 End With

Este segmento de código ilustra el modo en que podemos modificar dinámicamente el contenido textual de Etiqueta y constituye la base para que este control deje de ser necesariamente estático. Mediante la manipulación del contenido de una variable (sEtqConten) y el acceso al modelo de objeto (y su atributo Label) podemos implementar el texto que deseemos sin necesitar crear tantos diálogos y etiquetas como información queramos dar. Esto es válido para personalizar dicho contenido, para modificarlo en función de determinadas condiciones o para mostrar sobre el mismo diálogo una sucesión de contenido informativo diferente.

En el último documento que te proporciono ejemplifico una secuencia de mensajes simples sobre un único diálogo que se presentan de forma sucesiva mediante un bucle (5).

Documentos:

NOTAS

(1) Cuando no se usa como función (MsgBox()), capacidad que no tiene la etiqueta.
(2) De hecho difícilmente estaría justificado dedicarle una entrada en este blog.
(3) Segundo(s) supuesto(s) del párrafo anterior: notas informativas sobre el proceso a seguir e información sobre resultados del procesamiento de datos. Esta segunda función no es muy frecuente, ya que lo normal es trasladar los resultados el propio documento sobre el que trabaja el script.
(4) Los dos primeros documentos son ejemplos de etiquetas estáticas, única (el primero) y múltiple (el segundo). Perfectamente podríamos hacer uso de MsgBox. Aunque usar etiquetas tiene ventajas de formato sobre MsgBox, en lo fundamental no nos aportan mucho más. Aunque esas ventajas pueden ser suficientes para preferirlas.
(5Para que se produzca el cambio de saludo es necesario cerrar el Diálogo tantas veces como ciclos tiene el bucle (3)

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