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)

No hay comentarios:

Publicar un comentario

Comenta esta entrada