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

martes, 24 de octubre de 2023

OOo Basic. Script

 Subrutinas creadas a partir de macros

Después de la introducción general al tema realizada en una [entrada anterior], disponemos ya de argumentos para tratar con más detalle esta cuestión: cómo transformar una macro simple en una subrutina.




Por recordar brevemente lo dicho en su momento, una subrutina no devuelve un dato o valor (lo que la diferencia de una función), sino que desarrolla una acción. 


La subrutina realiza una acción, momento en el que el control de proceso pasa a ella. Esta imagen representa el proceso, incluyendo esa especie de corte en la ejecución del proceso principal, momento en que entra en acción la subrutina, la cual, una vez finalizado su cometido, devuelve el control a la macro o script principal.

Visto y recordado esto, nos centrarnos ahora en el tema que nos ocupa ahora: ¿ qué procedimiento o procedimientos podemos seguir para transformar una macro en una subrutina?.

Esta pregunta se responde en plural: existen varios procedimientos y diferentes variaciones de cada uno de ellos. Yo me voy a limitar a diferenciar y exponer tres de ellos, los que considero que se ajustan mejor a la macro originaria. Según este criterio serían tres las variaciones fundamentales entre las macros, lo que da origen a tres procedimientos básicos de conversión a subrutina: 
  • Las macros que contienen explicitación de parámetros (matriz nombre-valor argsX(y))
  • Las macros que son susceptibles de ser ejecutadas mediante estructuras cíclicas (for...Next), esto es: de repetir la acción que desarrollan.
  • Y las macros susceptibles a ser tratadas mediante la condicionalidad (If)
Veamos cada una de ellas, empezando por las macros más simples que incluyen parámetros y en las que tomo como ejemplo la macro de posicionamiento [listado numérico] (1): 
  • matriz desglosada clave+valor:
dim args1(0) as new com.sun.star.beans.PropertyValue
	args1(0).Name = "On"
	args1(0).Value = true
  • Orden o instrucción asociada a dispatcher:
dispatcher.executeDispatch(document, ".uno:DefaultNumbering", "", 0, args1())

Es el contenido o valor del componente .Value (en el ejemplo anterior true) el que podemos establecer como variable-parámetro en la creación de la subrutina basada en macros de este tipo, procediendo como sigue:

  • Denominamos la subrutina añadiendo un paréntesis al final de su enunciado
Sub msbIndiceNumerico()

  • Declaro la variable-argumento bOpcion, que es de tipo booleano, como corresponde en función del valor del elemento de la matriz args1(0).Value

Sub msbIndiceNumerico(bOpcion As Boolean)

  • Y sustituyo el valor true asignado en la macro simple original por dicha variable

args2(0).Value = bOpcion

El resultado...

Sub msbIndiceNumerico(bOpcion As Boolean)

dim args1(0) as new com.sun.star.beans.PropertyValue
	 args1(0).Name = "On"
args1(0).Value = bOpcion

dispatcher.executeDispatch(document, ".uno:DefaultNumbering", "", 0, args1()) 

End Sub

... es una subrutina que puede ser llamada (2) desde una macro o script principal como alternativa a la llamada opcional a dos macros simples: la de aplicación y la de retirada del índice numérico (3).

Una variación del modelo anterior es la que incluye el uso de un bucle en el script desde el que se llama a la subrutina, generando así una secuencia de desarrollo o aplicación de la subrutina. Por ejemplo, podemos avanzar letra por letra (incluyendo los espacios en blanco) seleccionando o no los elementos que se recorren. [Esta subrutina es un ejemplo de ello].

También podemos utilizar este bucle dentro de la subrutina, afectando únicamente a los instrucciones de ejecución (dispatcher), [como en este ejemplo].

sub msbAvanPag(iNumPag As Integer)

Dim i As Integer
For i=0 To iNumPag
dispatcher.executeDispatch(document, ".uno:PageDown", "", 0, Array())
Next

end sub

La tercera forma de crear una subrutina a partir de una macro simple ya vulnera el criterio de macro simple, al menos en alguna de sus formulaciones, pero también puede ser una solución cuando el uso de dos parejas de macros simples sea requerido con cierta frecuencia.

Considero "parejas de macros simples" cuando éstas son complementarias una de la otra, como en el caso que sigue:

sub mcrBajarLinea

dim args1(1) as new com.sun.star.beans.PropertyValue
args1(0).Name = "Count"
args1(0).Value = 1
args1(1).Name = "Select"
args1(1).Value = false
dispatcher.executeDispatch(document, ".uno:GoDown", "", 0, args1())

End Sub

Sub mcrSubirLinea

dim args1(1) as new com.sun.star.beans.PropertyValue
args1(0).Name = "Count"
args1(0).Value = 1
args1(1).Name = "Select"
args1(1).Value = false
dispatcher.executeDispatch(document, ".uno:GoUp", "", 0, args1())

End Sub

Las macros mcrBajarLinea y mcrSubirLinea presentan una doble complementariedad: ambas pueden tener valora interno de args1(1).value true o false (se selecciona o no la línea) y ambas son complementarias entre sí (o subimos o bajamos de línea): uno:GoUp vs. uno:GoDown.

Existen varias formas de crear una subrutina, pero me voy a limitar a dos, que son variaciones sobre el mismo tema. La primera se basa en el uso del condicional If e implica en realidad usar dos ejecutores dispatcher dentro de la misma surutina... 

sub msbLinea(bSelect As Boolean,sMov As String)

dim args1(1) as new com.sun.star.beans.PropertyValue
args1(0).Name = "Count"
args1(0).Value = 1
args1(1).Name = "Select"
args1(1).Value = bSelect
If sMov = "B" Then
dispatcher.executeDispatch(document, ".uno:GoDown", "", 0, args1())
MsgBox "He bajado una linea"
ElseIf sMov = "S" Then
dispatcher.executeDispatch(document, ".uno:GoUp", "", 0, args1())
MsgBox "He subido una línea"
Else
MsgBox "El valor de la variable 'sLinea' no es correcto" 
End If

End Sub
 ... que es llamada desde el script (msbLineaBis(true,"B")) de forma simple (4)

La segunda forma simplifica la formulación de la subrutina...

sub msbLinea(bSelect As Boolean,sMov As String)

dim args1(1) as new com.sun.star.beans.PropertyValue
args1(0).Name = "Count"
args1(0).Value = 1
args1(1).Name = "Select"
args1(1).Value = bSelect
dispatcher.executeDispatch(document, sMov, "", 0, args1())

End Sub

... y pasa la responsabilidad de definir concretamente la instrucción a  ejecutar a la llamada desde el script (msbLineaBis(true,".uno:GoUp")(5)

Te dejo aquí enlace a la [segunda opción], ya que la considero la que mejor se ajusta al principio básico de conversión de macro simple en subrutina.

NOTAS

(1) Existen muchas otras macros simples que presentan esta estructura y que pueden ser convertidas en subrutinas siguiendo este mismo procedimiento. Una de ellas, y en la que su conversión en subrutina es especialmente recomendable, es la macro de escritura (también podemos hacer lo mismo con el script OOo Basic alternativo a la macro). Si ahora propongo esta macro de posicionamiento es precisamente para evitar utilizar siempre el mismo ejemplo, aunque posiblemente sea éste un ejemplo no tan explícito como al que sustituye.

(2) Recuerdo los dos modos de usar la subrutina desde la macro principal:

  • El más simple: incluir el valor deseado en la llamada a la subrutina de la macro (msbIndiceNumerico(true)). Este modo de uso, además de ser la más simple, es el más apropiado cuando la macro o script es a su vez sencillo.
  • Y el modo complejo que recomiendo cuando el script es complejo e interesa controlar los valores que se pasan a las subrutinas y a las funciones, y que consiste en lo siguiente:
    • En esa macro/script declaramos una variable del mismo tipo que la variable-parámetro de la subrutina, por ejemplo, Dim bIndice as Boolean
    • Damos valor a esa variable según lo que deseemos hacer (en esta caso, aplicar o eliminar el índice numérico): bIndice = true
    • "Llamamos a la subrutina incluyendo entre paréntesis la variable bIndice: msbIndiceNumerico(bIndice)
(3) Dado que estamos trabajando con estructuras basadas en macros, es necesario utilizar las variables de objeto y los objetos asociados a ellas. He concretado esto como script VarSis. Este script puede ser llamado desde la subrutina o desde el script o macro que la utilice. [Ver como ejemplo].

(4) Si se desea esta llamada puede plantearse de modo interactivo, mediante InputBox() u otra GUI. Aquí omito estas opciones por ser secundarias para el objetivo de la entrada.

(5) En este caso es aun necesario, o cuanto menos conveniente, descomponer el proceso de asignación del segundo valor en dos fases: en la primera se asigna valor (puede hacerse de forma interactiva como en 4) y en la segunda, mediante condicional, dar a la segunda variable de la llamada la expresión técnica precisa. De este modo se evita la comisión de algún error en la asignación del dato (vg, .uno:GoUp)

sábado, 29 de julio de 2023

Lenguajes. OOo Basic.

Macro, subrutina y función

Para entender en qué consisten las funciones hay que tener en cuenta dos cuestiones: las instrucciones específicas que incorpora todo lenguaje para realizar determinadas tareas y el modo en que programamos los script.



La primera de estas cuestiones nos lleva a pensar en las funciones que incorpora todo lenguaje y que sirven para realizar "funciones" concretas y específicas. Un ejemplo son las funciones de conversión de tipos de variables, como CInt() que sirve para convertir una variable de cualquier tipo (string, por ejemplo) a numérica tipo Integer.

La segunda cuestión nos lleva a plantearnos los modos de programar, cuestión ésta de mayor complejidad conceptual y práctica que la anterior, pero ineludible a partir de cierto nivel de extensión y complejidad del código.

La forma de programar básica puede describirse como lineal: el conjunto de tareas que se definen en el algoritmo se convierten en líneas de código que se van sucediendo una tras otra hasta la conclusión del proceso.

Un ejemplo de ello es la forma de programar en el lenguaje Basic al inicio de la popularización  de los ordenadores. Otra, sin ir muy lejos, la que supone grabar una macro mediante Grabar macro.

Otra forma de plantearse la creación de programas se basa en reutilizar segmentos de código para simplificar el programa y hacerlo más legible y funcional. La forma más sencilla en que se manifiesta esta opción consiste en llamar a un script desde otro script, algo que también podemos hacer con macros. 

Sub EscribirMsgA
Dim sMsg As String
sMsg = "Hola Mundo 1"
MsgBox sMsg,64, "MENSAJE"
End Sub

 

Sub Receptor1
MsgBox "Mensaje desde script",64,"RECEPTOR 1"
Call  EscribirMsgA
End Sub

En este caso el script EscribirMsgA es funcional en si mismo,  pero también podemos reutilizarlo desde un segundo script (Receptor1llamándolo mediante la expresión Call, de la que podemos prescindir ya que se da por supuesta. Y lo dicho: también podemos hacerlo con una macro en sustitución del script EscribirMsgAaunque en este caso para escribir un texto directamente en el documento Writer.

A pesar de las ventajas de esta opción, también  tiene sus limitaciones; y la más importante es quizá, que es necesario modificar (en este ejemplo) el texto del mensaje en el script (o en la macro) subordinado, lo que en programas largos supone mucho trabajo e incrementa la probabilidad de cometer errores. 

Frente a estas opciones, funcionales pero limitadas, tenemos las subrutinas y las funciones. Empezaremos por aclarar en qué se diferencian ambas.

En OOo Basic las funciones se diferencian de las subrutinas claramente: mientras que las funciones devuelven  un valor (un  dato) y las subrutinas hacen algo, así que lo que "devuelven" es eso que hacen.

Veamos cómo crear una subrutina a partir del script anterior:

Sub EscribirMsgB (sMsg As String)
MsgBox sMsg,64, "MENSAJE"
End Sub

 

Sub Receptor2
MsgBox "Mensaje desde script",64,"RECEPTOR 2"
        EscribirMsgB ("Hola Mundo")
End Sub

Vamos a analizar ahora las diferencias entre EscribirMsgA (script) y EscribirMsgB (sMsg As String) (subrutina), aunque en cosa de denominación podrás comprobar que existe cierta confusión: en la documentación se suele emplear indistintamente el término macro, pero yo prefiero diferenciar entre macro, script (o rutina) y subrutina (1).

  • El script auxiliar (el primero) funciona con independencia del script principal (el segundo); la subrutina no.
  • La subrutina incorpora un identificador de variable en el paréntesis que lleva tras el nombre. En el script no existe dicho paréntesis y, en caso de identificarse una variable asimilable a la que declara la subrutina (es el caso de EscribirMsgA) se declara dentro del script y asigna contenido también dentro del script.
  • Mientras que en la llamada al script desde el principal (Receptor1) simplemente se llama al script auxiliar (Call  EscribirMsgA), al ser llamada la subrutina es necesario añadir un paréntesis tras el nombre y un contenido, en este caso un texto  (EscribirMsgB ("Hola Mundo")(2)

También podemos convertir las macros simples (algunas de ellas) en subrutinas, pero no en funciones, ya que las macros siempre hacen algo. Para ello, y en primer lugar, tenemos que diferenciar las que llamamos variables de sistema (que son de tipo objeto) de las que utilicemos para dar contenido concreto a la macro. A estas segundas son a las que daremos el tratamiento de subrutina, ya que las primeras, es posible darlas ese tratamiento, pero también otros, siendo preferible optar por uno más propio del trabajo con macros simples. Lo veremos a continuación en el ejemplo que vamos a desarrollar en base a la macro de escritura de texto.

Sub EscribirMsgC  (sTexto As String)
Dim document   as object
Dim dispatcher as object
document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
Dim args2(0) as new com.sun.star.beans.PropertyValue
args2(0).Name = "Text"
args2(0).Value = sTexto
dispatcher.executeDispatch(document, ".uno:InsertText", "", 0, args2())
End Sub

 

Sub Receptor3
MsgBox "Mensaje desde macro",64,"RECEPTOR 3"
EscribirMsgC ("Hola Mundo")
End Sub
  • Puedes observar que he formulado la macro del mismo modo que la subrutina. Gracias a ello, de hecho, he convertido la macro en una subrutina, aunque sería más correcto decir que esta formulación está a medio camino entre macro y subrutina, ya que mantiene componentes y formas propias de las macros, incorporando otras propias de la subrutinas. También sus "limitaciones": no funciona autónomamente, debiendo ser llamada desde un script "principal". 
  • El script "receptor" trata la macro como subrutina, tal y como podemos observar en la forma en que la llama (EscribirMsgC ("Hola Mundo"))

Frente a las subrutinas, en OOo Basic las funciones se diferencian claramente ya en su formulación: al denominarlas (apertura y cierre) se debe usar el identificador Function:

Function AreaCuadrado( iLado As Integer) As Double
    Dim dArea as Double
    AreaCuadrado = iLado * iLado
End Function

Esto es:
    • La función se declara con la expresión Function y finaliza con  End Function.
    • La función no funciona independientemente, necesita ser "llamada" desde un script principal.
    • Además del paréntesis donde se declaran las variables, también finaliza con una tipificación (As Double) que permite entender qué tipo de valor devuelve (numérico Double, en este caso) así como que la propia función es una variable, como de hecho lo es (o como tal se comporta), ya que devuelve un valor (el que contiene como variable)
    • El resultado del procesamiento interno de la función se asigna a la misma función a modo de variable (volvemos al punto anterior): AreaCuadrado = iLado * iLado. Otra formulación no provoca que la "función" dé error, pero tampoco devuelve nada.
Veamos cómo se formula el que llamamos script principal:

Sub Cuadrado
Dim sResultado As String
Dim iLong As Integer
Dim dArea
iLong = InputBox ("Dime el lado del cuadrado")
dArea = AreaCuadrado(iLong)
sResultado = "El cuadrado de lado " & iLong & " tiene un área de " & dArea
MsgBox (sResultado,64,"AREA DEL CUADRADO")
End sub

Puedes observar que la "llamada" a la función se diferencia de la llamada a la subrutina en que en la primera utilizamos la asignación a una variable, lo cual no hacemos en la llamada a subrutina puesto que la función es un valor (se comporta como una variable que tiene asignado un dato o valor), por lo que debe ser recogido por (y asignado a) una variable. Después podrá ser tratado como dato, tal y como sucede en este ejemplo en su concatenación en la variable string sResultado (3)

NOTAS

(1) Las funciones se diferencian claramente, por lo que no existen estos problemas terminológicos.
(2) Podríamos sustituir el texto por una variable, pero en este ejemplo he optado por la formulación más sencilla. En ese caso, dicha variable es diferente de la que se declara en la subrutina, aunque termina cumpliendo la misma función que aquella. Esta equivalencia viene facilitada por la funcionalidad compartida de ambas: complementan la forma en que se escribe la subrutina.
(3) En la que contradigo lo que he considerado conveniente en otra entrada: no convierto las variables numéricas en string, como sería recomendable, por lo que una buena formulación de esta instrucción sería la siguiente: sResultado = "El cuadrado de lado " & CStr(iLong) & " tiene un área de " & CStr(dArea)

sábado, 11 de marzo de 2023

Documentos. Acreditación

Acreditación -7. Docap complejo (d1)

Iniciando con lo prometido nos adentraremos ahora en el análisis del conjunto de script  que conforman esta docap.


Podemos ver en la imagen que sigue los tres módulos que componen la biblioteca Standard y los script individuales que contienen. 


  • Tenemos, en primer lugar, un módulo con script auxiliares cuyas funciones indican sus propios nombres. Dado que no se trata de script centrales del docap actual, no nos detendremos a explicar su funcionamiento.
  • El módulo CapturaDatos reúne los script cuyo cometido es recopilar la información que aporta el usuario o usuaria (fase input)
  • Y el módulo CreaDocum contiene el script que se ocupa de trasladar los datos anteriores al documento Writer que nos sirve de base para crear la acreditación.  

Además del módulo auxiliar, esta estructura revela la existencia de un proceso dividido en dos partes: el acceso a los datos y su ubicación controlada en celdas del soporte Calc y la captura del contenido de las celdas y su traslado al documento Writer.

En esta entrada nos centraremos en los script que forman parte del módulo CapturaDatos: DadosDocum y DatosAlumn; esto es, en la captura de datos del usuario 

Documentos: