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

viernes, 15 de marzo de 2024

Usos. Macros.

Uso de macros desde script (2)

La esencia de este procedimiento es la misma que la vista en la entrada anterior, pero la diferencia es que ahora los script son más genuinamente tales, esto es: en ellos se hace uso de los elementos del lenguaje (OOo Basic, en nuestro caso), lo que permite un mejor aprovechamiento de las propias macros y, sobre todo, un funcionamiento más funcional a la vez que complejo del algoritmo.


Este modo de trabajo supone, incluso, recurrir a OOo Basic para realizar ciertas modificaciones en las propias macros. Aunque esto no obligatorio (podemos seguir trabajando con las macros tal y como "salen del horno" de Grabar macro), hacerlo mejora sustancialmente la propia funcionalidad de la macro, por lo que es muy recomendable.

Lo que pretendemos ahora es dar más flexibilidad al conjunto (también a las macros), apoyándonos en los elementos del lenguaje OOo Basic y en lo conocemos de su sintaxis. Todo ello lo encontraremos en el contenido de esta página, como sabes.

Por comodidad y coherencia, partiré del conjunto de macros creados con motivo de la entrada anterior (1), incluyendo una macro simple de escritura para dar más funcionalidad a los script. Podríamos partir de un conjunto de macros simples diferentes, pero para nuestro objetivo, más que suficiente, es conveniente utilizar las anteriores macros.

Lo primero que vamos a hacer (2), es simplificar el código de las macros, aprovechando lo que sabemos sobre el uso de variables públicas y privadas. Como ves, al inicio de cada macro, Grabar macro genera un código básico que se repite en todas las macros, y en la misma posición. Estas líneas de código son las que llaman a dos objetos básicos para el funcionamiento de la macro: el documento activo y la funcionalidad lanzador de macro ("com.sun.star.frame.DispatchHelper") que Grabar macro almacena en dos variables que llamaré de sistema: document y dispatcher.

Este código repetido en cada macro es el siguiente:

dim document   as object
dim dispatcher as object

rem get access to the document
document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

Y  como se repite siempre que creamos una macro, podemos considerar estas dos variables como públicas, por lo que las declararemos al inicio del módulo, fuera de las macros, para que sean accesibles a todas ellas y crearemos un script (sub VarSis) específico (3) con la asignación de los objetos a las variables. Esto se concreta en el código siguiente:

'Declaración de variables de sistema para macros

dim document   as object
dim dispatcher as object

'Subrutina de asignación de objetos a las variables de sistema para macros

Sub VarSis
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
End Sub

Ahora ya podemos simplificar y reducir la extensión del código, eliminando esos elementos de cada macro y sustituyéndolos por una llamada a VarSis (4) como, por ejemplo (5):

sub Inicio
VarSis
dispatcher.executeDispatch(document, ".uno:GoToStartOfDoc", "", 0, Array())
end sub

Aunque al ser la creación de macros un proceso automático, por lo que no tenemos que escribir nosotros ni una sola línea de código y, por ello, simplificar las macros (lo que acabamos de hacer) no nos ahorra tiempo; al contrario, supone un trabajo, sencillo, pero trabajo, sobre todo si las macros son muchas; recomiendo hacerlo por las ventajas que supone en términos de legibilidad y funcionalidad del código en su conjunto. De todas formas, si no lo hacemos no se va alterar el funcionamiento de las macros ni de los script que las empleen; se ahorra algo de tiempo de ejecución y de memoria RAM, pero resulta imperceptible, dada la velocidad de procesamiento y capacidad RAM de los ordenadores actuales.

Lo que ya no es opcional si queremos aprovechar los conocimientos que tenemos sobre el uso de OOo Basic es trabajar de forma que, integrando varias macros, podamos generar resultados que sin este uso de OOo Basic no lograríamos con macros o resultaría mucho más costoso. Además, en cuanto a lógica de programación es mucho más interesante crear este tipo de script que limitarse a llamar a macros.

Vamos a ver una de las formas de hacer uso de OOo Basic para crear un script que, basándose en macros y haciendo uso de variables y un ciclo, nos permita adaptar nuestro código de forma que resulte más útil e interesante.
  • Primero. Modificamos la estructura de determinadas macros (en este caso las que vamos a usar) (6) transformándolas de hecho en subrutinas (tienen la misma estructura que una subrutina (7). Veamos un ejemplo:
sub LetraTipo (sLetraT As string)

Varsis

dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = sLetraT
args1(0).Value = true
dispatcher.executeDispatch(document, ".uno:"+sLetraT, "", 0, args1())

end sub

  1. Tras  el nombre (LetraTipo) abro y cierro paréntesis y en su interior declaro la variable sLetraT como string: sub LetraTipo (sLetraT As string)
  2. En args1(0) sustituyo el dato original ("Bold") por la variable sLetraT: args1(0).Name = sLetraT. De este modo, en lugar de un valor fijo, puedo trabajar con los valores que asigne a la variable sLetraT.
  3. Procedo del mismo modo en el ejecutor o lanzador de la macro, sustituyendo el segundo argumento por una cadena de texto resultante de concatenar un texto fijo con la variable anterior. El resto de los argumentos no sufre variación: dispatcher.executeDispatch(document, ".uno:"+sLetraT, "", 0, args1()) (8)

  • Segundo. Creo un script en OOo Basic cuyo funcionamiento esperado es el siguiente:
      • Se sitúa el curso al inicio del documento 
      • Se escribe una frase en negrita.
      • Se produce un determinado número de saltos de línea
      • Y se escribe una segunda frase también en negrita

    Este script que contiene variables a las que se asignan valores; estas variables se pasan como argumentos a la hora de llamar a las macros-subrutinas, las cuales asumen la función de los argumentos que se declaran en dichas macros-subrutinas (ver Primero)

    Para generar los saltos de línea se hace uso de un bucle For que se inicia en 0 y se repite  tantas veces como el valor numérico que se asigne a iNumLineas (4 en nuestro caso). Tras este salto de línea se ejecuta de nuevo llamada a la macro-subrutina EscribirPalabra(), asignando previamente un string diferente a la variable sTexto (argumento de la macro-subrutina EscribirPalabra().

    Podemos pensar en otras formas de utilizar macros como elementos de script; esta es una de ellas, pero no la única. Por ejemplo, siguiendo la misma línea de trabajo que hemos desarrollado en este script, podemos hacerlo interactivo, solicitando al usuario valores de entrada para crear otros texto. Para ello usaríamos la función InputBox(), por ejemplo. Pero lo hecho es suficiente para mostrar las posibilidades que tiene esta forma de hacer uso de la combinación de macros, macros convertidas en subrutinas y script.

    Te dejo a continuación acceso al documento que contiene las macros y el script explicados en esta entrada.


    NOTAS.

    (1) Lo recomendable es ir creando macros simples, eso sí, adaptadas según se explicará en esta entrada, y guardarlas en el IDE en Mis macros y diálogos, para que sean accesibles desde cualquier script.  Los script, por el contrario, posiblemente sea preferible almacenarlos en el propio documento, aunque no siempre, ya que depende del uso que se pueda hacer de ellos: si van a ser de uso frecuente en diferentes documentos, es preferible guardarlos también en Mis macros... Esta ubicación es muy recomendable (en esos casos) para el usuario que crea el código, pero imposibilita compartir con otras personas. En ese caso, se proporcionará el código por separado y será el usuario final quien decida donde lo copia y almacena.

    (2) En realidad es lo segundo, ya que la primera acción debe ser (ahora ya muy recomendable) limpiar el código de las macros de código basura, tal y como se explicó en la entrada anterior.

    (3) Este script de asignación puede tener cualquier otro nombre que deseemos darle, no necesariamente VarSis. Eso queda a elección del programador, pero, obviamente, la llamada que después debemos realizar a ese script debe ser coherente con el nombre que le demos.

    (4) Observa que no necesario utilizar la expresión Call para llamar al script. es suficiente con escribir el nombre del script para que sea llamado. No obstante, el uso de Call facilita la lectura del código ya que denota que se está llamando a una subrutina externa.

    (5) En realidad no es necesario llamar a VarSis desde cada macro, es suficiente con llamar a este script desde el script con que queramos manejar las macros. Sí es conveniente hacer incluir en las macros la llamada al script VarSis si queremos mantener la autonomía de cada macro, por si ocasionalmente necesitamos activarlas de forma directa (e individual), desde la funcionalidad  Macros/Ejecutar macro del menú Herramientas.

    (6) Los cambios de nombre que realizo respecto al original son para dar más coherencia al significado de las macros respecto a lo que hacen.

    (7) Recuerda que en OOo Basic se diferencia entre subrutinas y funciones, pero en otros lenguajes (Python, por ejemplo) se llama función a ambas, diferenciándolas por su comportamiento: una subrutina realiza una acción y una función devuelve un dato. Esta denominación puede dar lugar a confusión, ya que, salvo que se especifique lo contrario, todos los script escritos en OOo Basic se inician por la expresión Sub, que deriva de Subroutine, que no necesita traducción.

    (8) Esto último no es necesario hacerlo en todas las macros (ver, por ejemplo, la transformación de la macro EscribirPalabra en subrutina), pero sí en este caso, ya que el contenido de ese segundo argumento contenía originalmente el valor .Bold ( ".uno:Bold"), lo que genera ahora conflicto con el diseño subrutina de la macro. Esto no implica necesariamente que genere error, pero sí puede alterar el funcionamiento del script.

    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, 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: