jueves, 30 de mayo de 2024

OOo Basic. Inteface.

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

OOo Basic. Interface

 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)

jueves, 23 de mayo de 2024

Documentos. Python.

Script de Python para crear documentos

Después de exponer el esquema general, voy a continuar por explicar cómo entiendo el modo externo (1), y más concretamente la subdivisión que aprecio dentro de lo que en ese esquema identifiqué como modo 1A.


Dentro de éste considero necesario diferenciar entre lo que es crear un archivo de texto plano, que puedo identificar con extensión .txt, pero también como .doc, .odt o cualquier otra compatible con los procesadores de texto (Word y Writer, para ser más concretos), aunque siguen siendo archivos de texto plano (a esto llamo modo 1A1), de lo que supone crear archivo doc, xls o dpf en sentido estricto (modo 1A2).

El primero modo (1A1) es en realidad una forma de "engañar" a writer para que acceda directamente al archivo odt (también puede acceder a doc, como ya sabemos), para poder trabajar con él, aunque también puede hacerlo con un archivo txt sin mayor problema.

Mediante el segundo modo (1A2)  creamos archivos "originales" MSO o Acrobat, no archivos de texto plano, y tanto doc como xls son accesibles a LO-Writer/Calc, pero por la compatibilidad de LO, no por efecto de Python. Además, en este caso (1A2), para crearlos necesitamos trabajar en Python con paquetes (librerías) específicos (from... import) que previamente deberemos haber instalado (pip install).

Dejamos (de momento) 1A2 y nos centramos en la forma más simple (realmente muy simple) de crear documentos accesibles desde LO-Writer. Y digo bien, desde Writer, ya que este modo no es funcional para crear archivos compatibles con Calc (sí posible, pero no funcional).

Habrá que diferenciar dos momento o fases dentro de esto modo 1A1:

  • La fase (A) de creación del archivo (de momento, para más detalle, ver  las entradas de ARCHIVO en este mismo blog) mediante script Python
  • Y la base (B) de trabajo con el archivo desde LO-Writer.
En sentido estricto no podemos decir que estemos generando un docap, no al menos en la forma en que lo hemos creado hasta ahora mediante OOoBasic; pero no por ello deja de ser una forma sencilla de utilizar Python como alternativa a OOo Basic, aunque yo diría que más que alternativa, complementaria.

Complementaria porque también OOoBasic, bien como macro (funcionalidad Grabar macro), bien como script, resulta, no imprescindible pero sí conveniente para llegar a un resultado que podemos considerar equivalente a la automatización de documentos mediante OOo Basic (docap). Eso sí, con una limitación: debemos ceñirnos a trabajar con texto plano, sin elementos gráficos (vg. tablas), al que opcionalmente podemos dar formato desde LO-Writer con macros.

Y digo con macros porque me atrevo a ir un poco más allá en recomendaciones: en el momento actual de mis conocimientos planteo como más adecuado complementar el proceso con el uso de macros en vez de script; es mucho más sencillo e igual de funcional, por lo que nos será mucho más fácil crear este tipo de recursos.

Manteniendo este principio de funcionalidad y sencillez, en el momento actual, para los primeros desarrollo del modo 1A1 planteo usar la consola (símbolo del sistema en Windows) como recurso input. Esto nos ahorra la complejidad que implica la creación e implementación de una GUI (vg., basada en tkinter) y permite que nos centremos en la lógica de programación. Además el uso del cmd (consola) es perfectamente coherente con la simplicidad que buscamos y a la que debemos aspirar como profesionales ajenos a la programación profesional, aunque usuarios competentes de un lenguaje de programación que priorizan la funcionalidad frente al diseño. 

Este enfoque es similar al que observo en profesionales de otros campos (la medicina, el derecho, las ingenierías...) que también priorizan en el uso de Python (también de OOo Basic) la sencillez y la funcionalidad sobre el diseño. Creo que esto va en sentido lo contrario a lo que entiendo se busca desde determinados enfoques del desarrollo competencial docente: frente al consumidor de productos que supone la apuesta de la Administración por los lenguajes de autor, la  competencia que facilita el pensamiento computacional que promueve el aprendizaje y uso de un lenguaje de programación orientado al logro de objetivos profesionales relevantes.

Dado que lo creado hasta el momento es muy básico (no busco ahora utilidad alguna) será suficiente con que accedas al script Python, y a la macro de LO (1)

Y por si te interesa profundizar en el contenido, la forma y el funcionamiento, aquí tienes el vídeo que complementa esta entrada.


NOTA 1. En realidad se trata de un archivo .txt que deberás descargar y copiar en el IDE de LibreOffice, Mis_macros_y_diálogos.

Lenguajes. Python.

Python desde dentro de Libre Office

No hace mucho descubrí un vídeo en Youtu.Be en el que se explicaba que Excel había incorporado Python como lenguaje de macros. Creí que se trataba de un vídeo de una (¿nueva?) biblioteca que permitía generar documentos Excel desde Python, pero no: se trataba del uso de Python como lenguaje "interno" para crear funciones. Este video data del 30 de agosto de 2023 y hace referencia a una nueva funcionalidad de Excel, así que es novedad. ¡Qué cosas! LibreOffice dispone de esta misma funcionalidad, para todos sus servicios desde que tengo memoria de haberlo utilizado, y de eso ya van unos cuantos años...

No quiero decir con esto que la implementación de Python en Excel funcione exactamente igual que como funciona en LibreOffice, ya que, al parecer el objetivo es mucho más concreto y posiblemente el resultado sea también más funcional, pero resulta cuanto menos curioso que MSO-Excel "copie" una funcionalidad disponible en LibreOffice (genérico), disponiendo como se dispone de librerías específicas que permiten a Python trabajar con y para Excel (también para Word, como hemos identificado en el modo 1A2 en nuestro esquema. Algo tiende de específico y especial  este modo que en ese mismo esquema denominé Modo interno 2B.


Si por algo se caracteriza este modo es precisamente por su similitud con las formas de uso de OOo Basic, sin duda el lenguaje de "macros" preferente de LO (igual de lo es VBA para MSO), así que Python se presente como alternativa a VBA desde Excel viene a corroborar lo que Maurico Baeza ya planteó hace tiempo respecto a LO: la utilidad de Python para extender las funcionalidades de LO, derivada de la versatilidad y potencia del lenguaje.

Esto no implica que OOoBasic pase a ser obsoleto, ya que, hoy por hoy sigue presentando (a mi entender) varias ventajas respecto a Python, por lo que (opino (1)) es conveniente seguir aprendiendo y trabajando con OOo Basic en sus dos formas (2): a partir de Grabar macro y directamente desde el IDE creando script.
  • De facilidad de aprendizaje.
  • De facilidad de generación, incluyendo la funcionalidad Grabar macro, no disponible para Python.
  • Y la facilidad de aplicación o uso de las macros o script, que se concreta en una mayor capacidad de integración en los servicios LO.
Parece ser que la posibilidad de utilizar Python desde (dentro de ) LibreOffice no hay que darla por echa, siendo necesario comprobar que es posible mediante un sencillo procedimiento que consiste en lanzar una "macro" desde un documento Writer (yo hablaré de script) de comprobación (3):
  • Desde Herramientas/Macros/Ejecutar macro->
  • Selector de macros -> Biblioteca "Mis Macros" ->
  • Carpeta "HolaMundo-> script HelloWorldPython-> Ejecutar
Si se escribe en el documento el texto "Hello World (in Python) es que tienes Python disponible como lenguaje de "macros". En caso contrario deberás realizar los cambios pertinentes en la configuración de LibreOffice, pero si trabajas desde Windows es poco probable que no esté ya correctamente configurado.

Superada la primera dificultad  toca ahora afrontar la segunda: si estás acostumbrado a trabajar con OOoBasic te sorprenderán las dificultades para para gestionar los script Python, ya que sólo es posible ejecutar los que están disponibles desde Mis Macros o desde Macros de la aplicación, pero no parece posible crear y grabar script propios en ninguna de las tres ubicaciones conocidas (añadiendo a las dos anteriores el propio documento).

Existen modos de sortear estas dificultades (4), pero por el momento me voy a limitar a explicar una solución sencilla, aunque no ideal: sigue esta ruta para acceder y grabar los script de Python...

C:\Users\NombreUsuario\AppData\Roaming\LibreOffice\4\user\scripts\python (5)

... que estarán después accesibles desde Mis Macros. Este directorio es accesible desde Herramientas/Macros/Ejecutar macro o desde Herramientas/Macros/Organizar macros/Python, que son también las formas más simples de ejecutar un script.

Bien, ya sabemos cómo activar un script Python desde LO-Writer (por ejemplo) y cómo guardar los que creemos nosotros, pero aun  nos queda aprender a crearlos.

En efecto, sabemos que podemos crearlos desde el IDE de Python, y que podemos guardarlos en la ubicación que indica la dirección antes mostrada, incluso visualizarlos desde un documento (por ejemplo, desde Writer), pero no será reconocido sin más por LO como archivo válido si no cumple determinadas condiciones. Esto es lo que ocurre con el script que creamos para trabajar el modo 1A1...

print("¿Cómo te llamas?")
nombre=input()
print(f"Me alegro de conocerte,{nombre}")

... que podemos guardar y visualizar, pero al que no podemos acceder desde Writer -> (...) Ejecutar/Organizar macro.

Vamos a ver cuáles son esas condiciones que hacen posible el funcionamiento de un script Python llamado desde un servicio LO; y para ello nada mejor que explorar un ejemplo de script Python ubicado en ese subdirectorio, ya que sabemos que funciona, por ejemplo, mismamente HelloWorldPython.

Si abrimos este archivo desde Bloc de notas (por simplificar), obtenemos un documento cargado de líneas comentadas (#) que por el momento podemos obviar, así que el script, sin comentarios, queda como sigue:

def HelloWorldPython():

    desktop = XSCRIPTCONTEXT.getDesktop()
    model = desktop.getCurrentComponent()

    if not hasattr(model, "Text"):
        model = desktop.loadComponentFromURL(
            "private:factory/swriter", "_blank", 0, ())

    text = model.Text
    tRange = text.End
    tRange.String = "Hello World (in Python)"
    return None

Por comprobar que todo funciona correctamente, podemos probar a guardar este archivo libre de comentarios con un nombre alternativo al original (HelloWorldBis.py) y comprobar sí funciona llamándolo desde Writer (Herramientas/Macros/Organizar macros/Python). Como es de esperar el resultado es positivo: se escribe en el documento la esperada frase Hello World (in Python).

Ya tenemos, pues, un script que no es reconocido y otro que sí y podemos ver que se diferencian en que en el segundo se utilizan instrucciones específicas, que sabemos forman parte de las propias de una librería especialmente diseñada en LO para hacer posible el uso de Python llamada PyUNO.
  • desktop.getCurrentComponent()
  • desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, ())
Algunas de ellas no te resultarán extrañas, dada su similitud con las empleadas en OOoBasic. Otras son específicas de PyUNO:
  • XSCRIPTCONTEXT.getDesktop()
He ahí una cuestión importante: si podemos utilizar Python desde LO es porque desde Python empleamos una librería específica, imprescindible que poder hacerlo. Esto es similar a lo que hacemos desde el modo 1A2 para crear documentos word o excel, como señalo en otra entrada (6)

Para finalizar esta entrada, y aunque queda mucho por aclarar, paso a exponer el código de un script elaborado por mí a partir del código de los que se presentan como ejemplo. Incluye líneas de comentario (en rojo) para facilitar su comprensión.

def texto_en_python():
    #Imprime un texto en un documento Writer situando el cursor al final del mismo.
    saludador = "Javier"
    texto_saludo = f"Saludos cordiales de parte de {saludador}, desde Asturias.\n"
  texto_contenido = f"Este escrito realizado por {saludador} es parte de un proyecto de aprendizaje del uso de python en LO\n"
   texto_explica = f"En este caso se trata de generar información textual en soporte writer desde cualquier servicio de LO"
    texto = texto_saludo + texto_contenido + texto_explica
#Llamada a la aplicación de texto de Libre Office
    desktop = XSCRIPTCONTEXT.getDesktop()
    model = desktop.getCurrentComponent()
#Entra en acción si la aplicación activa no es Writer
#En ese caso deriva a la creación de un nuevo documento writer
    if not hasattr(model, "Text"):
        model = desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, ())
    text = model.Text       #Llama a la subclase Text del componente actualmente activo
    tRange = text.End       #Ubica el puntero al final del documento
#Y escribe el contenido de la variable (podría escribir directamente un string)
    tRange.String = texto

Libre Office cuenta con otros ejemplos de código Python que son de interés para comprender las posibilidades del uso "interno" este lenguaje en la creación de script, así que en futuras entradas profundizaremos en la comprensión de estos ejemplo como paso previo para la creación de código propio. Pero por ahora es suficiente.

NOTAS

NOTA 1. Propongo que al menos hasta alcanzar un nivel de competencia asimilable al que podemos haber adquirido en OOoBasic, que se manifieste en ser capaces de generar soluciones basadas en Python similares a los docap creados mediante OOoBasic. Es un límite asequible y fácilmente comprobable, por lo que no supone una generalidad inconcreta.

NOTA 2Eso que en el esquema  se denomina modo interno 2A

NOTA 3. Creo que se explica en el vídeo de Mauricio Baeza que enlacé antes.

NOTA 4Incluyendo la instalación de extensiones que se pretenden simplificadoras de todas estas limitaciones (como la extensión APSO), pero son estos temas suficientemente complejos como para que lo más prudente en estos momentos iniciales los dejemos aparcados. Tiempo habrá de tratarlo más adelante.

NOTA 5Users puede aparecerte directamente como Usuarios y debes sustituir NombreUsuario por tu verdadero nombre de usuario. Además es posible que cuando llegues a este punto no encuentres visible la carpeta AppData (no confundir con la nombrada como Application Data); esto es debido a que está oculta. En ese caso vas al menú Vista y activas la casilla  Elementos ocultos. A partir de ahí puedes seguir la ruta indicada tanto para acceder a los script ya creados como para copiar los que tu crees, en principio desde el IDE que proporciona Python por defecto, desde el mismo bloc de notas o desde cualquier programa de edición de código (Thonny, por ejemplo, por empezar por uno sencillo).

NOTA 6. Ese modo de trabajo externo no ha sido explicado aun, dado que en este momento he priorizado la simplicidad a la secuencia de la categorización del esquema. Para mayor conocimiento de estos recursos acceder a la documentación de Foundation Wiki y la ayuda de LibreOffice y los enlaces a otros documentos que contienen estas dos referencias

miércoles, 22 de mayo de 2024

Lenguajes.Python.

Instalar paquetes mediante PIP

Se puede decir que cuando instalamos Python instalamos lo justo y necesario para empezar a trabajar, pero Python y su comunidad de usuarios dispone de un muy importante volumen de recursos, disponibles de formar gratuita y destinados a cubrir múltiples necesidades. Para acceder a ellos necesitamos instalarlos previamente. Para ello disponemos de diferentes medios, uno de ellos el instalador  PIP.

Como primera referencia te propongo este artículo de Bustamante S.J.(2021), publicado por freecodecamp.org en enero de 2021, que contiene la información necesaria para aprender a trabajar con el instalador pip.

También me ha parecido interesante y clarificador el vídeo de Errodringer del que además del enlace anterior, te dejo acceso directo a continuación. 



Creo que ambos contenidos son más que suficiente para el objetivo de esta entrada: saber en qué consiste la instalación de librerías y módulos, y cómo llevarla a cabo. Además me liberan de tener que hacer más extensa esta entrada.


lunes, 20 de mayo de 2024

OOo Basic. Interface.

Formularios (c). Calc

También en LO.Calc podemos utilizar formularios sin necesidad de acceder a la capa gráfica (getDrawPage()). Esto es posible por permitir LibreOffice la vinculación de los controles de formulario (algunos) con las celdas.



Cuando implementamos un control en una hoja de cálculo podemos definir sus características desde Propiedades del control (1). Este menú consta de 2 ó 3 submenús, en el segundo de ellos (Datos) podemos incluir, si es que está disponible, la referencia a la celda (vg. A7) con la que vinculamos el control desde la opción Celda enlazadaEsta opción está disponible para los cuatro controles que vimos en la [entrada anterior], estos es, para los campos Cuadro de texto, Cuadro de lista, Botón de opción y Casilla.

Al establecerse esta asociación (2) podemos acceder al contenido creado por el usuario accediendo al contenido de la celda vinculada, evitando así el acceso a la capa gráfica del documento. Como sabemos (3) para acceder al contenido de una celda deberemos seguir lo pasos siguientes:

Dim oDocumento As Object, oHojaActiva As Object, oCelda As Object

Dim sContenido As String

oDocumento = ThisComponent 

oHojaActiva = oDocumento.getCurrentController.getActiveShee()

oCelda = oHojaActiva.getCellRangeByName("E3")

sContenido = oCelda.getString()

La repetición de las líneas 5-6 de este script (con la creación de tantas variables Object y String como controles-celdas tenga nuestro formulario, nos permitirá acceder al conjunto de datos introducidos por el usuario.

Aunque el procedimiento básico es así de simple, deberemos considerar, no obstante, las peculiaridades y/o posibilidades que presentan los distintos tipos de control, ya que lo anterior es válido para el control Cuadro de texto, y también para el resto, pero cada uno de los restantes presenta alguna peculiaridad que es necesario conocer.

El control Cuadro de lista permite en su configuración seleccionar, además de la celda enlazada, el contenido que mostrará dicha celda. Esta opción está disponible desde Datos | Contenido de la celda enlazada, pudiendo establecer bien La entrada seleccionada o bien la Posición de la entrada seleccionada (4). Aunque en este ejemplo y en la mayoría de los formularios con los que trabajemos, la opción preferente será la primera (La entrada seleccionada), tanto por funcionalidad como por facilidad de uso en el script (5), habrá ocasiones en las que sea conveniente trabajar con Posición de la entrada seleccionada precisamente por tratarse de un valor numérico (6). En este caso tanto la variable que asociemos al contenido de la celda, como el tratamiento posterior de esta variable (y su contenido) será muy diferente del que normalmente haremos con el texto resultante de la opción La entrada seleccionada (ver nota 4).

El o mejor aun, los controles Botón de opción y Casilla presentan también peculiaridades  que es necesario conocer, aunque en este caso se trata tanto de la vinculación a las celdas como del tratamiento del resultado de la elección del usuario y su constatación en el contenido de las celdas.

Es necesario saber, en primer lugar, que cada botón o cada casilla se debe asociar con una celda diferente. Esto tiene sentido para las casillas, pero puede parecer paradójico para los botones, ya que éstos están vinculados y la elección de uno implica necesariamente la no elección del resto (7). No obstante, la lógica de la asociación control-celda impone que se vincule cada botón a una celda, ya que si vinculamos todos los botones a la misma celda lo que hacemos (por la bidireccionalidad explicada en nota 2) es precisamente equivalente a seleccionar todos los botones.

Lo segundo que debemos saber es que, en coherencia con la naturaleza de los botones  y las casillas, lo que ambas devuelven (y queda escrito en las celdas enlazadas) en un valor booleano ("VERADERO" | "FALSO"), lo que conlleva repercusiones tanto para el acceso al contenido de dichas celdas como para el posterior tratamiento de estos datos. Se abren además diferentes opciones de trabajo para el desarrollo de este tratamiento. Veamos las implicaciones de lo anterior en nuestro formulario de ejemplo.

En  él contamos con dos botones para la elección del sexo del alumno (por ejemplo), cada uno de ellos asociado a una celda (por las razones antes dichas), concretamente btnM está vinculado a F15 y btnF a F16. En ambos casos, en dichas celdas se escribe de forma simultánea VERDADERO y FALSO (o viceversa) según pulsemos en el primer o en el segundo botón (8). Ahora bien, a nosotros lo que nos interesa es lo que deriva de la elección del usuario, esto es: conocer si el sujeto referenciado es niño o niña (9) y nada de eso nos dice el contenido de F15 o de F16 (10). Para convertir VERDADERO | FALSO en niño | niña necesitamos emplear una estructura o función condicional, pudiendo optar por emplear las funciones Calc o por el tratamiento condicional (if) desde OOo Basic.

En el primer caso (que es lo que hacemos en nuestro ejemplo) construimos sobre la celda G15 la siguiente función: =SI(F15=1,"niño";"niña) (11). En el segundo accederemos mediante código al contenido de la celda F15 y construiremos una estructura condicional como se ejemplifica a continuación, y que cumple la misma función (12):

Dim oCeldaSexo As Objet

Dim sSexo As String

oCeldaSexo = oHojaActiva.getCellRangeByName("F15")

If oCeldaSexo.getValue = 1 Then (Ver nota 11)

sSexo = "niño"

Else

sSexo = "niña"

End If

También en Casilla sucede algo similar. En nuestro ejemplo se utilizan tres casillas, cada una de ellas asociada a una celda (F19, F20 y F21 respectivamente) que contienen valores booleanos VERADERO | FALSO. Y también en este caso disponemos de dos opciones para convertir estos valores en el resultado que nos interesa (en este caso la puntuación del ítem).

La opción basada en las funciones Calc se desarrolla en dos fases:

  • En la primera puntuamos cada ítem mediante una función condicional (=SI(F19="";"";SI(F19=0;1;0))) sobre las celdas G19, G20 y G21.
  • Y en la segunda calculamos el total de puntuación del ítem mediante la función Calc Suma() (=SUMA(G19:G21))
Dadas las características propias de Casilla tenemos que tomar en cuenta la acción del usuario sobre cada una de las casillas que componen este elemento del formulario, siendo esta una diferencia con el tratamiento del control Botón. Por ese mismo motivo necesitamos realizar la segunda fase (el sumatorio), ya que el análisis de la casilla individual no nos permite acceder al resultado del conjunto. Esto también es válido para la opción basada en OOo Basic, como veremos a continuación.

Dim oCeldaItem1 As Object,oCeldaItem2 As Object,oCeldaItem3 As Object

Dim iPuntos As Integer

iPuntos = 0

If oCeldaItem1.getValue = 0 Then

    iPuntos = iPuntos + 1

End If

If oCeldaItem2.getValue = 1 Then

    iPuntos = iPuntos + 1

End If

If oCeldaItem3.getValue = 1 Then

    iPuntos = iPuntos + 1

End If

Puedes observar como, en esta modalidad u opción necesitamos tres variables Object, una para cada Casilla (oCeldaItem1, oCeldaItem2 oCeldaItem3), además de una variable Integer para el sumatorio de la puntuación. Además, cada variable Ítem debe ser sometida al análisis condicional (vg. If oCeldaItem1.getValue = 0) y el cálculo de la puntuación total (el sumatorio) se realiza asignando repetidamente (tantas veces con ítem y condicionales) la propia variable a si misma más el punto que corresponde por acierto (iPuntos = iPuntos + 1) (13)

Documento.

NOTAS

(1) Sobre la implementación manual de un formulario en un documento ver [esta entrada]

(2) La asociación o vinculación entre el control y la celda es bidireccional; en consecuencia, lo que escribamos o seleccionemos en el control se escribe en la celda y viceversa. Lógicamente también lo que se modifique o se borre.

(3) Ver procedimiento en [esta entrada]

(4) En el primer caso mostrará en la celda asociada el texto seleccionado de la lista de opciones que tenga establecido Cuadro de lista (vg. 1º EP), y en el segundo, para esta misma elección del usuario, mostrará en la celda el valor numérico 2, ya que se trata del 2º elemento de la lista de opciones. La consecuencia es tanto para la visualización del resultado como para el procedimiento de acceso al contenido de la celda enlazada, ya que la variable receptora deberá ser (preferiblemente) de tipo numérico; pero también puede tener consecuencias para el posterior tratamiento del contenido de la variable con la que asociemos este dato.

(5) Esta opción tiene la ventaja de permitir un tratamiento directo del contenido textual, pero también la total similitud del proceso con el que realizamos con Cuadro de texto

(6) En ejemplo de esto es cuando la elección del usuario implique valor cardinal u ordinal, como en el caso de las escalas tipo Likert. Estas escalas pueden construirse utilizando diferentes controles; Cuadro de lista es uno de ellos. 

(7) Cosa que, como sabemos, no ocurre con las casillas, dado que es es posible seleccionar cada casilla de forma independiente de lo que hagamos con el resto. En este caso es lógico que cada casilla deba estar asociada a una celda distinta, pero el valor final de una elección entre varias opciones excluyente de un botón (entre varios), sólo puede arrojar un único dato.

(8) Esta simultaneidad es resultado de la vinculación entre ambos botones; es coherente con su lógica de funcionamiento y, en consecuencia, síntoma de una correcta configuración del conjunto. 

(9) En el supuesto, por ejemplo, de que tal categorización tiene efectos en las formas gramaticales de un texto que describa el resultado de un análisis de datos o una comunicación personalizada con la familia, etc. 

(10) Obviamente, a excepción de lo que nos permite la visualización directa del formulario; pero estamos hablando aquí de la automatización del análisis de los resultados, no del que podemos realizar manualmente visualizando los datos que se nos muestran en la hoja de cálculo.

(11) No es necesario trabajar con el contenido de la celda F16 dada la asociación que se establece entre los botones de opción. Ver explicación en el texto de la entrada, contenidos asociados a las notas y 8, y en las propias notas. Observa que la expresión "VERADERO" es sustituida por 1. Esto es debido a la equivalencia entre 1 -> VERADERO y 0 -> FALSO cuando se trata de datos booleanos. Lo mismo sucede en el código alternativo OOo Basic (If oCeldaSexo.getValue = 1 Then)

(12Queda a la tu decisión optar por una u otra opción, pero es necesario que conozcas ambas para que estés en posición de elegir. En ambos casos el resultado es el mismo. Mi recomendación es que, dado que optamos en este caso por acceder a los resultados mediante el acceso a las celdas (y no directamente a los controles), continúes con esa misma lógica y utilices las funciones Calc todo lo que te sea posible. Es necesario saber utilizar estas funciones y en este caso hacerlo no sólo mantiene la coherencia con el planteamiento de trabajo, además supone un ahorro de tiempo.

(13) Este proceso puede resolverse de otras formas, pero para los que nos interesa ahora es suficiente con la forma en que aquí se desarrolla. Respecto a la opción a elegir (el uso de funciones Calc vs. código OOo Basic remito a lo dicho en la nota 12.