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

domingo, 29 de junio de 2025

LibreOffice. Calc.

Celdas. Acceso al contenido.


Para acceder al contenidos de una celda utilizamos diferentes funciones en OOo Basic, la más común, pero no la única es getString().


Gracias a la función getString() podemos pasar a una variable (o a un componente de una matriz) el contenido de una celda que se sea texto. 

sNombre=oCelda.getString()

Pero no es la única función, ya que también disponemos de getValue() para cuando el contenido es un número.

iDato=oCelda.getValue()

Podemos conocer si ese contenido es textual (vs. numérico) usando las funciones Calc ESTEXTO() (que devuelve True/VERDADERO vs. False/FALSO) o ESNUMERO() (de igual pero inverso resultado). 

Como alternativa a estas funciones Calc podemos utilizar la función OOo Basic getType() que devuelve un valor numérico (integer) que nos informa de qué tipo de contenido tiene la celda, dato que nos permite usar la primera o la segunda función OOo Basic:
  • Valor 0 para celda vacía
  • Valor 1 para celda con texto (string)
  • Valor 2 para celda con número (integer)
  • Valor 3 para celda con fórmula como contenido
Por ejemplo...

iTipo = oCelda.getType()

If iTipo = 0 Then

MsgBox("La celda está vacía")

ElseIf iTipo = 1 Then

 Nombre=oCelda.getString()

ElseIf iTipo = 2 Then

 iDato=oCelda.getValue()

End If

 

martes, 17 de junio de 2025

Datos. Python.

Módulo ezodf (Calc)


Para trabajan con documentos Excel disponemos de la biblioteca Openpyxl y para trabajar con Calc del módulo ezodf (1). Este módulo está menos desarrollado que el anterior, pero nos sirve para un manejo básico de archivos sencillos.


Igual que sucede con Openpyxl, también deberemos instalar el módulo ezodf mediante el instalador pip (pip install ezodf) y después importarlo en nuestro script (import ezodf).

Con este módulo podemos hacer unas cuantas cosas, algunas de las cuales son accesibles en forma de ejemplos desde GitHub (2), pero no me voy a detener demasiado en esto ya que la utilidad inmediata que le veo a este paquete, igual que a otros de tipo ofimático, es facilitar la automatización de tareas, y escribir en Calc desde Python no me resulta especialmente atractivo.

Me limitaré entonces a mostrar como acceder a un archivo Calc y leer datos de sus celdas. Es una tarea muy simple pero muy interesante, ya que nos va a permitir acceder a nuestros documentos Calc y a los datos que contienen para trabajar con ellos de diferentes maneras (3)

Veamos a continuación un pequeño script Python:

import ezodf

# Lectura del archivo ODS
documento = ezodf.opendoc('AlumLS.ods')

#Acceso a la hoja Datos 1 (la segunda, en este caso)
hoja_datos = documento.sheets[1]

#Acceso a la celda A1
celdaA1 = hoja_datos['A1'].value

#Mostrar contenido de la celda
print(celdaA1)

Como podemos ver este script no tiene ningún misterio y no es muy diferente del que podemos crear con la misma función mediante Openpyxl. De hecho la función clave opendoc() es muy simple e intuitiva y el resto del procedimiento tampoco se puede considerar novedoso: vamos descendiendo desde el documento hasta la celda y una vez en este nivel, nos apropiamos de su contenido (4) para asociarlo a una variable que después podemos manipular en función del objetivo que persigamos.

No corresponde ahora realizar este tipo de desarrollos, pero en entradas posteriores mostraré alguna de estas posibilidades.

NOTAS

(1) Muy recomendable consultar la [página del proyecto]
(2) Especialmente ver [documentación y ejemplos]
(3) En concreto estoy pensando en el acceso masivo a datos y la creación de procedimientos de análisis mediante bibliotecas especializadas de Python; pero también en cuestiones más simples, como la automatización de la creación de documentos Excel a partir de documentos Calc. Hasta donde yo se, no existe una biblioteca que convierta directamente un tipo de documento en el otro y por desgracia, los módulos Python tanto de acceso a datos como de manipulación y análisis de los mismos están diseñados para trabajar con formatos Excel.
(4) Importante observar que debemos especificar que accedemos al valor o dato almacenado en la celda. Para ello añadimos al identificador de la celda el atributo value (hoja_datos['A1'].value)

lunes, 2 de diciembre de 2024

Textos. Automatización.

Soluciones basadas en utilidades LO


Aunque no constituyen realmente una solución de automatización, entre otras cosas porque no implican el uso de código, contamos en LO con dos opciones que contribuyen al desarrollo de este proceso y que podría formar parte de eso que llamamos competencia ofimática en el nivel usuario avanzado. Me estoy refiriendo al uso de formularios y de la funcionalidad Combinar correspondencia.


La
creación de un formulario es un proceso que nos permite convertir un documento de texto en una herramienta de recogida de datos, pero también nos ayuda a formalizar, estandarizar y simplificar la elaboración de ciertos documentos de trabajo. Ejemplos de ellos los ofrece la propia Administración que, en esto sí que ha desarrollado ciertas iniciativas, como puede ser documentación que formaliza el proceso de derivación que llamamos Solicitud de evaluación (psicopedagógica), pero que tiene quizá como mejor exponente el documento-soporte Dictamen de escolarización, que no es más que un formulario complejo y de cierta extensión.

Estos ejemplos evidencian también los propios límites de ese esfuerzo (1), ya que se trata de soportes que en realidad no facilitan su propio uso en la medida en que esto podría ser posible. Ello es debido a que se formaliza el documento, pero no se emplean las herramientas realmente disponibles, recurriendo al uso de tablas (mayormente) en lugar de emplear controles de formulario.

Cierto que la parte más compleja del trabajo ya nos viene dada, pero falta dar ese segundo paso, que es el que realmente facilita el trabajo con el documento: la implementación de los controles de formulario. Gracias a ellos (y con independencia que el soporte definitivo se concrete como documento para ser usado desde el procesador de textos o como archivo pdf) podremos acceder a los campos que, dada la diversidad de opciones tipológicas disponibles, pueden ahorrar trabajo concretando las formas e incluso definiendo contenidos concretos mediante alguna forma de selección. Desde [este enlace] puedes acceder a un ejemplo de conversión de un documento tabulado en un formulario.

De todas formas, como dije, aun en su formulación más básica y menos eficiente, la presentación de un documento como formulario es ya un avance nada desdeñable para el desarrollo de la lógica de programación, así que trabajar en esta dirección (y hacerlo además empleando los controles de formulario) es dar pasos adelante en la misma dirección que en este blog se plantea.

Lo es por la sencilla razón de que al establecer un campo estamos definiendo la esencia de una variable, componente básico de todo script y tarea que deberemos desarrollar para crear soluciones basadas en programación. Falta mucho camino por recorrer y podemos quedarnos por el camino (y para prueba todos los botones que nos proporciona el Servicio de la Consejería con sus no demasiado elaborados modelos documentales), pero también podemos seguir trabajando y en ese trabajo lo aprendido con la creación de formularios nos va a ser muy útil.

No obstante, aunque facilite el trabajo y sea un aprendizaje necesario, de por sí, un formulario no nos permite avanzar en el objetivo de la semi-automatización de la documentación, cosa que se sí es posible mediante Combinar correspondencia, pero con importantes limitaciones.

supone automatizar la elaboración de documentos, ya que emplean explícitamente referencias a campos (variables) que se complementan (personalizan) directamente mediante su asociación a una tabla de datos (2). De este modo podemos automatizar la elaboración de múltiples documentos basados en un modelo único y hacerlo mediante un sólo clic. Pero tiene una limitación, una seria limitación, que hace que esta funcionalidad sea poco funcional, la cual deriva de la función (disculpas por tanta repetición del término) para la que está diseñada: mandar la misma información (formalizada) a múltiples destinatarios; algo así como crear etiquetas para sobres, mandar la misma carta, pero personalizada, a muchas personas y objetivos similares.

De esto se deriva que, además de lo que supone conectar el documento con la base de datos y de los fallos que se pueden producir (y se producen) en este proceso, sólo sea una solución viable para el trabajo con documentos sencillos y de escasa extensión; más allá de estas limitaciones, el trabajo se complica mucho y el rendimiento real del soporte suele ser muy inferior al esperado, y más aun si tenemos en cuenta el exigente trabajo previo que requiere. De todo esto se deriva que el uso de esta alternativa sea muy limitado (3).

No obstante, además de que de los errores también se aprender, este procedimiento también contribuye a desarrollar el pensamiento computacional, incidiendo también en el uso de variables, claramente diferenciadas aquí del cuerpo de información "constante". También ayuda a entender que, aunque la idea de extender el trabajo con variables a algo más que personalizar un documento sencillo es una buena idea, el procedimiento que permite Combinar correspondencia no es suficientemente flexible, siendo necesario buscar una alternativa que lo sea, y que de paso evitar las complicaciones que derivan de la conexión documento-fuente de datos.

NOTAS

(1) Para ser justos debo decir que de "ese pecado" no estamos libres tampoco los SEO, ya que no es infrecuente que reproduzcamos el mismo error: creamos "formularios .doc" basados en tablas y no se nos ocurre que podemos implementar controles de formulario que nos van a ahorrar mucho trabajo a la hora de usarlos. También de esto tenemos [algún ejemplo].
(2) La cual se puede basar en una tabla Calc o en una base de datos auténtica.
(3) Motivo por el que sólo te puedo [mostrar aquí un ejemplo], añadiendo como advertencia que no resulta fácil de manejar, ya que conexión del documento-base de datos se pierde al cambiar las condiciones originales en la que se estableció esa conexión.

Textos. Automatización.

Tipología documental y enfoques de automatización


Establecer una tipología documental es necesaria para facilitar el proceso de automatización, aunque sean muchos los criterios en los que nos podemos basar para realizar esta tipología y el resultado sea necesariamente insuficiente e insatisfactorio.


Uno de esos criterios, relevante pero no el único ni necesariamente el mejor, es la diferenciación en el grado de formalización del documento, que nos permite establecer un recorrido desde la ausencia de formalización (mínima formalización) y la asimilación del documento a un formulario (máxima formalización). Esta diferenciación puede ser poco relevante en términos de contenido, pero es totalmente pertinente en cuestiones de forma y de grado de prescripción, dada la coincidencia entre formalización y principios de práctica normativo-burocrática de la Administración.

La forma es determinante para el desarrollo de planteamientos técnicos, tanto para definir su viabilidad como para determinar las estrategias de trabajo, de modo que condicionan los enfoques de automatización. veámoslo con más detalle.

Un texto no formalizado o escasamente formalizado permite (¿obliga?) a emplear estrategias basadas en la identificación variables vs texto fijo, lo que conlleva el enfoque de trabajo basado en referencias o modelos sobre los que construir a su vez el modelo textual-base sobre el que aplicar (sugiero) el procedimiento texto-cloze como enfoque procedimental de automatización.

Por el contrario, la automatización de un texto fuertemente formalizado deberá basarse en el enfoque Combinar correspondencia y sus desarrollos. Una propuesta concreta podría ser el uso de marcadores. En cualquier caso es fundamental contar con una base de datos, por lo que se hace necesario implementar procedimientos para generar dicha fuente de datos, aunque no forme parte del proceso de automatización de documentos (de textos, sería más preciso decir) en sentido estricto.

Pero dado que el grado de formalización es entendido aquí como un continuo, los textos realmente susceptibles de automatización presentan diferentes grados o nivele de formalización, por lo que cabe desarrollar (por ejemplo) esos enfoques de automatización en diferente grado o medida. En esto tiene un peso fundamental el modo en que se plantea el trabajo con las variables, siendo una de sus posibilidades el tratarlas como elementos organizados y de referencia para la toma de decisiones. De ello podríamos derivar un tercer enfoque (en realidad, un enfoque complementario de los anteriores) no tanto de automatización en sentido estricto como de uso de los recursos que facilitan dicha automatización: esto es, de los lenguajes de programación y el enfoque algorítmico de trabajo que representa (pensamiento computacional).

Debo decir que soy consciente de la dificultad que todo esto tiene para los profesionales de los SEO, que no son ni tienen por qué ser programadores; pero también lo soy de las posibilidades que derivan de que sean los SEO los protagonistas del desarrollo de este proceso (la automatización de documentos) por motivos que ya comenté en otros momentos y sobre los que probablemente volveré en esta sección. Una forma de acortar el recorrido que separa ambas dimensiones del problema es recurrir a las herramientas que ponen a nuestra disposición suites ofimáticas como LibreOffice, tanto las que incorpora como funcionalidades como las disponibles como lenguajes de script, como OOo Basic. Por aquí me propongo empezar; parece razonable hacerlo, dada la familiaridad práctica que estas herramientas tienen para nosotros por motivos de trabajo.  

jueves, 19 de septiembre de 2024

LibreOffice. Calc

Intercambio de datos entre matrices.

Con independencia del modo en que hayamos conseguido ingresado los datos en una matriz, hay veces que nos interesa utilizar estos datos para pasárselos a otra. En esta entrada vamos a analizar diferentes situaciones, posibilidades y procedimientos de uso de una matriz como fuente de datos de otra.


Cierto que podemos utilizar un listado Calc como fuente de datos, y que la presentación de la lista en columna o fila es una forma visual de mostrar el contenido de una una matriz simple, por lo que la primera forma de pasar datos de una matriz-fuente a otra (matriz-recurso, podríamos decir) es utilizar los procedimientos vistos en [esta entrada anterior], pero precisamente lo que nos interesa tratar en esta entrada es el trabajo con matrices definidas mediante código, por lo que no se contempla el acceso a fuentes de datos "externas", sino el traslado de datos entre matrices en sentido estricto.

En ese sentido, la forma y circunstancia más simple se da cuando queremos duplicar, por algún motivo los datos originales de una matriz en una segunda; la primera sirve de fuente original de datos (matriz-fuente) y la segunda (o las segundas) como matrices de trabajo (matriz-recurso), sobre la(s) que ejecutaremos diferentes acciones, sean éstas las que sean (1).

En estas circunstancias, OOo Basic nos permite utilizar una forma muy simple y eficiente de paso de datos: es suficiente con asignar la matriz-fuente a la matriz-recurso, del mismo modo que podemos asignar una variable a otra:
  • vSegunda = vPrimera
  • mRecurso() = mFuente() 
En ambos casos, siempre que la variable y la matriz original contenga datos (2), estos pasarán a la variable o matriz a que se asignan. Lo interesante es que en el caso de las matrices este paso se hace directamente, sin que sea necesario hacerlo dato a dato mediante un bucle, que sería la forma esperada (3).

No obstante, este método sólo es útil cuando queremos trasladar a la matriz-recurso todos los datos (y en el mismo orden) que contiene la matriz-fuente, pero no sirve si sólo queremos trasladar una parte de ellos, por ejemplo, los situados en las posiciones 3-5 (4). En ese caso deberemos utilizar un bucle For realizando previamente un redimensionado de la matriz receptora (5):

End Sub

Sub Matices2

Dim mFuente () As Integer, mRecurso() As Integer, i As Integer

mFuente = Array(1,2,4,8,16,32,64,128)

ReDim mRecurso(2)
For i = 3 To 5
mRecurso(i-3) = mFuente(i)
Next

MsgBox "Visualizar datos de mFuente()"
For i = LBound(mFuente()) To UBound(mFuente())
MsgBox mFuente(i)
Next

MsgBox "Visualizar datos de mRecurso()"
For i = 0 To UBound(mRecurso())
MsgBox mRecurso(i)
Next

End Sub

El script anterior es un ejemplo de cómo podemos pasar a la matriz-recurso (mRecurso()) un subconjunto de los datos de la matriz-fuente (mFuente()). Observa que ninguna de las dos están dimensionadas al ser declaradas, pero mientras que mFuente() queda dimensionado mediante Array() (mFuente = Array(1,2,4,8,16,32,64,128)), mRecurso() debe debe serlo explícitamente mediante la instrucción ReDim (ReDim mRecurso(2)). El ciclo For que sigue nos permite definir qué elementos de la primera pasaremos a la segunda; para ello es fundamental el valor de inicio y de fin que demos al contador i (For i = 3 To 5, en este caso) (6).

NOTAS

(1) Un motivo puede ser que la primera (matriz-fuente) contenga los datos de partida que nos interese conservar sin modificaciones para poder realizar diferentes operaciones con ellos, siendo las matrices-recurso los soportes sobre los que realizar dichas operaciones. Una de ellas (matrices Integer), producir un incremento o un decremento de valores, otra con ellas (matrices-String) la selección de un subconjunto de datos y/o la eliminación del resto. En ambos casos, si trabajáramos con la matriz original perderíamos los datos iniciales, que son precisamente los que nos interesar conservar para realizar diferentes operaciones.
(2) Ya he explicado en varias ocasiones que esta es una forma de hablar para simplificar el discurso, pero que en realidad tanto las variables como las matrices no son otra cosa que etiquetas que facilitan al script el acceso a direcciones de memoria RAM que contienen los datos que son referenciados por los nombres o identificadores de las variables y de las matrices.
(3) No obstante, las similitudes de manejo variables-matrices no van mucho más allá. Por ejemplo, en el caso de un intercambio de datos entre variables, es posible ejecutar directamente una operación sobre la variable-origen para que el valor de la variable-recurso incorpore el efecto de esa operación directamente (vSegunda = vPrimera + 2), cosa que no se puede hacer en el caso de las matrices (mRecurso() = mFuente()+2): en este caso la operación genera error, así que nos exige realizar el proceso en dos pasos, el primero la asignación y el segundo desarrollado mediante un bucle, por ejemplo, sobre los elementos de mRecurso().
(4) Recuerda que las matrices inician en la posición 0, por lo que la posición 3 se corresponde con el cuarto elemento de la matriz.
(5) Aunque la tendencia es declarar las matrices con un valor numérico (ver nota 4), lo cierto es que podemos declararlas sin definir su tamaño. La matriz-fuente tendrá el tamaño que definamos mediante Array(). Para otros modos de trabajar con la matriz, deberemos redimensionarla previamente a la introducción de datos mediante la instrucción ReDim.
(6) Obsérvese el valor que se le da al índice de mRecurso() (mRecurso(i-3)) para el correcto posicionamiento de los datos en función del tamaño de la matriz. Los dos ciclos que siguen sirven para comprobar el contenido de ambas matrices, por lo que no son relevantes en esta explicación.

miércoles, 18 de septiembre de 2024

LibreOffice. Calc.

Uso de matrices para acceso a datos.

Aunque ya hablamos de las matrices como [colecciones de datos] como elementos (y objetos) de OOo Basic (1), son muchos y diferentes los usos que podemos hacer de una matriz; no sólo sobre Calc, pero sí especialmente por el manejo de los datos que permite una hoja de cálculo. Una de ellas es dotar de contenido a una matriz pasándole los datos de un conjunto de celdas, generando así un código que se puede asimilar al trabajo con bases de datos. En esta entrada nos vamos a limitar a explicar cómo utilizar las matrices para acceder al contenido de un listado previamente creado en una hoja de cálculo, presentación de información ésta muy frecuente en este servicio, como ya sabes (2).


Ya sabemos cómo implementar datos a una matriz directamente desde código mediante la función Array() (mDatos() = Array(1,2,3,4,5) como ejemplo simple) y como hacerlo usando el índice del elemento (mDatos(1) = 1) (3). Pero cuando tenemos una lista o una tabla de datos ya creada, usar matrices para acceder a los datos que contiene dicha tabla nos permite trabajar posteriormente con su contenido. 

En realidad el procedimiento no difiere mucho de hacerlo mediante variables, pero cuando los datos son muchos, usar variables es poco eficiente.

Para el objetivo de esta entrada vamos partir de una tabla previamente creada en una hoja de cálculo y suponer que deseamos pasar su contenido a un conjunto de matrices para trabajar con esos datos (4). La tabla que tomo como referencia es la siguiente (5)


Como puedes ver, se trata de una simple tabla de datos de alumnos (por ejemplo, del alumnado NEAE de un centro) y deseamos capturar estos datos para mostrar una ficha individual por alumno (6). Podríamos trabajar con variables, accediendo secuencialmente a cada una de las celdas, y mostrando en bucle el contenido de cada registro de esta base de datos; aunque para un objetivo tan simple como el enunciado podría ser una solución bastante satisfactoria, pero si quisiéramos tener disponibles todos los datos de la tabla para tareas posteriores, trabajar con variables nos obligaría a generar tantas como celdas (campos) hay en la tabla, lo que hace poco recomendable, por no decir inviable trabajar con variables (7). Afortunadamente podemos hacerlo empleando matrices. Veamos cómo.

Como casi siempre , no hay una única solución, pero no todas son iguales o igual de eficiente. Aquí veremos dos, la primera, menos eficiente, pero necesaria para comprender la dinámica de trabajo y la segunda, más eficiente y más compleja, aunque  no especialmente difícil. Empecemos por la primera.

Sub CrearMatrizDesdeDatos

'Variables para acceso a celdas
Dim oHoja As Object
Dim oCelda As Object

'Variables para ciclos y tratamiento
Dim i As Integer
Dim vTratam1 As String, vTratam2

'Matrices para recogida de datos
Dim mAlId (19) As String, mAlSexo(19) As String, mAlNom(19) As String, mAlAp(19) As String, mAlCurso(19) As String

'Acceso a datos
oHoja = ThisComponent.getSheets().getByName("Datos")
'Ciclos para pasar datos de celdas a las matrices

For i= 0 To 19
oCelda = oHoja.getCellRangeByName("A" & i+2)
mAlId (i) = oCelda.getString()
Next

For i= 0 To 19
oCelda = oHoja.getCellRangeByName("B" & i+2)
mAlSexo (i) = oCelda.getString()
Next

For i= 0 To 19
oCelda = oHoja.getCellRangeByName("C" & i+2)
mAlNom (i) = oCelda.getString()
Next

For i= 0 To 19
oCelda = oHoja.getCellRangeByName("D" & i+2)
mAlAp (i) = oCelda.getString()
Next

For i= 0 To 19
oCelda = oHoja.getCellRangeByName("E" & i+2)
mAlCurso (i) = oCelda.getString()
Next

 (Aquí va un bucle que permite visualizar el resultado)

End Sub

Aunque creo que los comentarios son suficientemente explicativos (al menos eso he pretendido), no estará de más explicar algunas cuestiones (8), al menos la principal. Me refiero a esa estructura cíclica que se repite cinco veces, una por cada columna de datos de la tabla. Veamos uno de estos bucles, el primero, por ejemplo:

For i= 0 To 19
oCelda = oHoja.getCellRangeByName("A" & i+2)
mAlId (i) = oCelda.getString()
Next

Haciendo uso de la estructura For...Next y del contador i repetimos veinte veces el siguiente proceso: 
  • Primero accedemos a las celdas mediante la variable objeto oCelda (oCelda = oHoja.getCellRangeByName("A" & i+2)). El posicionamiento en las celdas se basa en la unión de la letra que identifica la columna (A)con la suma del valor de i+2 ("A" & i+2) Esto se explica por ser el valor inicial de i=0 (For i= 0 To 19) y la posición de la primera celda significativa A2.
  • La segunda instrucción del ciclo pasa el dato contenido en las celdas a los elementos definidos en la matriz receptora (mAlId (i) = oCelda.getString()), cada uno de ellos identificado por el valor de i a lo largo del ciclo (mAlId (i))
El hecho de que esta estructura se repita cinco veces da una idea de cómo podemos mejorar la eficiencia de este procedimiento: dado que se repite el procedo podemos pasarlo a una función que lo asuma, reduciendo el script principal a una mera llamada a la función. Esta será la segunda solución.

Sub CrearMatrizConFuncion

'Variables para ciclos y tratamiento
Dim i As Integer
Dim vTratam1 As String, vTratam2

'Matrices para recogida de datos
Dim mAlId (19) As String, mAlSexo(19) As String, mAlNom(19) As String, mAlAp(19) As String, mAlCurso(19) As String

'Llamada a función para cargar datos en las matrices
mAlId() = PasarDatos("A")
mAlSexo() = PasarDatos("B")
mAlNom() = PasarDatos("C")
mAlAp() = PasarDatos("D")
mAlCurso() = PasarDatos("E")

(Aquí va un bucle que permite visualizar el resultado)

End Sub

' FUNCIÓN --------------------------------------

Function PasarDatos (Col As String) As Object

'Variables para acceso a celdas
Dim oHoja As Object
Dim oCelda As Object
'Matriz para recogida de datos
Dim mDatos (19) As String
'Variable para recorrer ciclo
Dim i As Integer

'Acceso a datos
oHoja = ThisComponent.getSheets().getByName("Datos")
'Acceso al contenido de la columna de datos
For i= 0 To 19
oCelda = oHoja.getCellRangeByName(Col & i+2)
mDatos(i) = oCelda.getString()
Next

'Paso de valores de la matriz
PasarDatos = mDatos()

End Function

Obsérvese cómo las llamadas a la función desde el script permiten simplificar el código anterior (mAlId() = PasarDatos("A")). El parámetro que pasamos a la función es el identificador de la columna ("A" en este caso) y al asignar el resultado que devuelve la función a la matriz deseada, asignamos los datos que a su vez captura la matriz-base de la función (mDatos()) a la matriz receptora (aquí mAlId()).

La función (PasarDatos (Col As String) As Object) es de tipo objeto por trabajar con el objeto oCelda (9) y asume el acceso al documento y a la hoja (oHoja = ThisComponent.getSheets().getByName("Datos")) y a los objetos Celda (oCelda = oHoja.getCellRangeByName(Col & i+2)), en este caso igual que vimos en el procedimiento primero, esto es, dentro de un bucle que recorre el conjunto de celdas de cada columna de datos. Además, al tratarse de un procedimiento "universal", sólo necesitamos una variable para acceder a los datos (Dim mDatos (19) As String), proceso que se realiza (también de forma similar a cómo vimos en el primer procedimiento), dentro del ciclo, como segunda instrucción del mismo (mDatos(i) = oCelda.getString()). Finalmente asignamos la matriz mDatos() como "Return" de la función (10)

Para finalizar y por eso de que no quede sin explicar, voy a comentar el ciclo en que se basa la generación del "informe" que nos permite comprobar que todo ha funcionado correctamente (11). Cumple, por tanto, función de generador del output y, como recordarás, es el mismo en ambas formulaciones de la solución al tema que se trabaja en esta entrada.

'Bucle para mostrar el contenido de las matrices
For i = 0 To 19
If mAlSexo(i) = "H" Then
vTratam1 = "del alumno"
vTratam2 = "Alumno"
Else
vTratam1 = "de la alumna"
vTratam2 = "Alumna"
End If
MsgBox "FICHA Nº " & i+1 & Chr(13) &_
   "Id " & vTratam1 & ": " & mAlId(i) & Chr(13) &_
       vTratam2 & ": " & mAlNom(i) & " " & mAlAp(i) & Chr(13) &_
       "Curso: " & mAlCurso(i)
Next

Puedes distinguir una estructura For...Next que incluye (anida) una estructura condicional (If) y un mensaje de cierta complejidad basado en un MsgBox.

El condicional utiliza los valores contenidos en la matriz mAlSexo() (columna B de nuestra tabla de datos) para condicionar las expresiones gramaticales ajustas al género gramatical de dos expresiones del mensaje. Estas fórmulas gramaticales quedan asociadas a las variables de tratamiento (vTratam1 vTratam2). Al preceder  el condicional al mensaje, cada recorrido del ciclo nos permite asignar el género gramatical adecuado al sexo del alumno.

El mensaje, además de ajustar las expresiones al género gramatical, resume los contenidos del resto de las matrices que hemos creado y por la propia estructura de la tabla y el modo en que "recuperamos" los datos, esos contenidos quedan asociados como si de un registro unitario se tratara gracias a que ocupan la misma posición en sus respectivas matrices. La impresión final que proporciona nuestro MsgBox es que mostramos la información de cada uno de los alumnos.

Documento. En [Matrices2.ods] puedes encontrar el código explicado en esta entrada.

NOTAS

(1) Además de la entrada anterior, tienes más información básica sobre las matrices en las entradas que siguen a la anterior en la sección [OOo Basic] de este blog. Te remito también a lo que aporta  Mauricio Baeza (2020) en [Aprendiendo OOo Basic], pag. 49 y siguientes.
(2) Dentro del escaso uso que se hace de las hojas de cálculo por parte de los SEO, quizá sea esta de crear listados y tablas una de las más frecuentes. A ello contribuye la demanda de datos por parte de la Administración, que con cierta frecuencia aporta incluso el modelo o soporte del documento, y la facilidad que presenta este soporte para realizar tareas de este tipo. En ocasiones estas tablas funcionan (o pueden funcionar) como bases de datos sencillas.
(3) Esta fórmula también nos sirve para acceder al contenido de una matriz por su índice para, por ejemplo, asignarlo a una variable (vDato = mDato(0))
(4) No entramos en cómo se creo esta tabla de datos y en estos momentos tampoco es relevante en qué consista y cómo se desarrolle el trabajo posterior con esos datos.
(5) Como puede suponer, los datos son inventados. Cualquier coincidencia con datos reales es eso, pura coincidencia.
(6) Como dije en 4, el tratamiento posterior de los datos es irrelevante para el objetivo de esta entrada, así que no te esperes maravillas al respecto. En realidad mostrar esos datos en forma de "Ficha personal" tiene la función de permitirnos comprobar que nuestro procedimiento funciona correctamente.
(7) Sin ser una tabla especialmente grande, necesitaríamos 20 x 5 = 100 variables, que no son pocas. Si incrementamos la tabla en 20 registros (filas) más, cosa nada improbable en cuanto a tamaño real de una base datos sencilla, el número total de variables ascendería a 200. No hacen falta tantos registros para que trabajar con variables deje de resultar viable.
(8) De momento dejaré sin explicar el bucle final que puedes encontrar en el script de la hoja de cálculo que adjunto. Este bucle se repite en ambos procedimientos y es secundario para lo que nos ocupa. Pero dado que, como dije en 6, sirve para comprobar el funcionamiento del procedimiento, no estará de más dedicarle un tiempo al finalizar la explicación de las dos versiones de la solución a nuestro problema.
(9) Siendo sincero, esta es la conclusión a la que he llegado, pero no tengo certeza absoluta de que esta sea la causa. Sí he comprobado que sin esta categorización de la función (por ejemplo, definiéndola como de tipo String, por ser de este tipo los datos que recuperamos de las celdas), se produce un error de ejecución.
(10) Lo de "Return" es una forma de significar la función de la instrucción PasarDatos = mDatos() como forma de retornar o devolver datos de las funciones en OOo Basic. En otros lenguajes, como Python, se emplea esa instrucción, pero en OOo Basic la fórmula consiste en utilizar el nombre de la función a mod de variable a la que se asigna el contenido a retornar. Por cierto que nuestra función nos permite comprobar que es posible devolver una colección de datos (esto es, más de uno) cuando utilizamos una matriz como contenedor/referencia.
(11) Repito aquí de ya dicho en las notas 4 y 6 y me ahorro más comentario.

jueves, 18 de julio de 2024

LibreOffice. Calc.

Calc. Borrar hojas.

Cuando el docap basado en Calc contiene una serie de hojas que nos interesan en un determinado momento, pero que resultan innecesarias una vez aplicado, resulta conveniente borrar las que no sean necesarias.


En este caso, siguiendo a Mauricio Baeza (1), para borrar hojas disponemos de la función removeByName(Nombre), que contiene el parámetro Nombre, lo que indica que el borrado está determinado por la identificación del nombre de la hoja. Un ejemplo

Sub BorrarHojaNombre

Dim oHojas As Object

oHojas = ThisComponent.getSheets()
oHojas.removeByName( "HojaFinal" )
End Sub

Como puedes imaginar, no es posible borrar una hoja que no exista (hacerlo provoca error del sistema), por lo que es conveniente controlar que se cumple dicha condición mediante un condicional asociado al método  oHojas.hasByName().

También podemos borrar la hoja activa, en la que previamente deberemos estar situados.

Sub BorrarHojaActiva

Dim oHojas As Object
Dim oHojaActiva As Object

oHojaActiva = ThisComponent.getCurrentController.getActiveSheet()
oHojas = ThisComponent.getSheets()
oHojas.removeByName( oHojaActiva.getName() )
End Sub

Dado que un libro debe contener al menos una hoja, si sólo tiene una o si hemos borrado previamente todas las anteriores, al intentar borrar esa única hoja el sistema dará error. 

Podemos controlar ambos errores (nombre de hoja inexistente o intento de borrado de hoja única) introduciendo las siguientes instrucciones en un condicional:

If oHojas.hasByName( sNombre ) And oHojas.getCount()>1 Then
oHojas.removeByName( sNombre )
Else
MsgBox "La hoja no existe o solo queda una"
End If

NOTAS

(1) Mauricio Baeza Servin (2007). Aprendiendo LibreOffice Basic. Documento modificado en septiembre de 2014.

Datos. OOo Basic

Crear hojas (b).

Además de lo [ya visto] sobre el tema, todavía quedan algunas cuestiones por explicar en lo que a la creación de hojas se refiere. En esta entrada retomaremos algunas cuestiones y trataremos sobre otras que son de interés para la automatización del manejo de hojas mediante OOo Basic.


Como dice Mauricio Baeza (1), para crear o insertar una hoja utilizamos el método InsertNewByName(Nombre,Posición). Los dos parámetros nos permiten dar nombre a la hoja y posicionarla respecto al conjunto existen. Un script de ejemplo (2):

Sub InsertarUnaHoja
Dim oHojas As Object
oHojas = ThisComponent.getSheets()
oHojas.insertNewByName("Hoja2", 1)
End Sub

El problema de este modo de crear una nueva hoja es que es un procedimiento único, debido a que no pueden existir más de una hoja con el mismo nombre. El parámetro posición no supone esa limitación, pero modifica la posición de las hojas creadas si se sitúa por delante de ellas.

Para crear un número de hojas sin incurrir en la limitación anterior, deberemos manejar el parámetro Nombre como un string resultante de una concatenación de cadenas, siendo la segunda de ellas un valor numérico incremental convertido a string mediante la función CStr(). Por ejemplo:

Sub InsertarVariasHojas
Dim oHojas As Object
Dim i As Integer
oHojas = ThisComponent.getSheets()
For i = 0 To 3
oHojas.insertNewByName("Hoja" & CStr(i),i)
Next
End Sub

Creamos tantas hojas como deseemos mediante un bucle For, utilizando la variable Integer i como índice que aplicamos tanto en el parámetro Nombre ("Hoja" & CStr(i)), como al parámetro Posicion. De este modo no se repite el mismo nombre de la hoja, evitando así el error de sistema.

Finalmente podemos insertar una hoja al final del conjunto existente de forma automática, esto es: dando por supuesto que desconocemos el número de hojas que contiene el libro Calc.

Sub InsertarHojaFinal

Dim oHojas As Object
Dim oHoja As Object
Dim sNombre As String

sNombre = Trim(InputBox("Nombre de la nueva hoja"))

If sNombre <> "" Then
oHojas = ThisComponent.getSheets()
If Not oHojas.hasByName(sNombre) Then
oHojas.insertNewByName( sNombre, oHojas.getCount() )
End If
oHoja = ThisComponent.getSheets.getByName(sNombre)
ThisComponent.getCurrentController.setActiveSheet(oHoja)
End If

End Sub

En este caso pedimos al usuario el nombre la hoja mediante InputBox (sNombre = Trim(InputBox("Nombre de la nueva hoja"))) y comprobamos que el usuario introduzca un nombre  mediante un condicional If (If sNombre <> "" Then). También comprobamos mediante condicional que el nombre de la hoja no se repite  (If Not oHojas.hasByName(sNombre) Then). El posicionamiento (al final) se consigue mediante el método getCount() que devuelve el número de hojas del libro. Finalmente nos posicionamos en ella (oHoja = ThisComponent.getSheets.getByName(sNombre)) y la convertimos en la hoja activa (ThisComponent.getCurrentController.setActiveSheet(oHoja))

NOTAS

(1) Mauricio Baeza Servin (2007). Aprendiendo LibreOffice Basic. Documento modificado en septiembre de 2014.
(2) Este procedimiento [ya se explicó aquí], pero conviene insistir en él para la mejor comprensión de lo que sigue.