4.2. Atributos como objetos ActiveX

Nuestro artículo anterior refería la manera en que Visual LISP es capaz de gestionar otras aplicaciones del entorno Windows. Continuamos ahora demostrando cómo es capaz también de acceder a la propia jerarquía de objetos de AutoCAD, gestionando sus métodos y propiedades.


El trabajar con bloques aporta eficacia al uso de un sistema CAD. En lugar de dibujar cada vez una entidad gráfica que se repite, podemos empaquetar sus componentes en un único objeto contenedor que se referencia desde cada uno de sus inserciones en el dibujo. Es decir que lo que aparece en cada ocurrencia del bloque es como una vista del paquete original, desplazada al lugar de la inserción, posiblemente girada y en algunos casos sometida a transformaciones de escala.

El circulo, el arco, la línea que vemos no es un objeto nuevo y distinto, sino una referencia a la definición contenida en el objeto Bloque original. De aquí que debamos distinguir entre dos objetos diferentes:

  • el objeto Definición de Bloque
  • el objeto Referencia de Bloque

El segundo es una copia geométricamente transformada del primero.

Un texto puede formar parte de un bloque, como cualquiera otra entidad gráfica. Pero en muchos casos es deseable que el texto de un bloque pueda cambiar para cada una de sus referencias en el dibujo.

La posibilidad de incluir como parte de una definición de bloque información alfanumérica variable para cada ocurrencia constituye una anomalía respecto a la definición estricta de bloque. Ya no se trataría de una simple copia de la definición original.

Para dar solución a este caso se recurre a un proceso similar al utilizado para generar el bloque mismo: se crea una entidad gráfica especial, la entidad Definición de Atributo, que actúa como plantilla, reservando un lugar y predefiniendo las características visuales que adoptará dicha información.

Esta viene a concretarse en su contenido literal sólo en el momento de generarse cada ocurrencia particular, es decir en el momento de la inserción del bloque, pasando a generar una entidad nueva, el objeto  Referencia de Atributo, asociado siempre al objeto Referencia de Bloque. La Referencia de Atributo se asocia a la Referencia de Bloque estableciendo una secuencia en la base de datos de entidades, de manera tal que este último objeto actúa como cabecera de una sucesión de Referencias de Atributo que viene a concluir con la entidad especial Fin de Secuencia. El comando DBLIST aplicado en un dibujo con bloques insertados permite comprobar este aspecto.

Acceso a los Atributos

El mecanismo habitual para recuperar los atributos desde AutoLISP ha sido el de recorrer linealmente la base de datos gráfica, partiendo de la entidad INSERT (Referencia de Bloque) mediante la función entnext hasta encontrar la entidad SEQEND (Fin de Secuencia). La Figura 1 muestra, a manera de ejemplo, el código de una función de este tipo.

Figura 1 Función recursiva LISP para extraer Atributos

El entorno ActiveX exige proceder de distinta manera. Los objetos Referencia de Atributo se han de recuperar mediante un método especializado del objeto Referencia de Bloque. Al recorrer las colecciones Blocks, ModelSpace o PaperSpace, pueden aparecer objetos Definición de Atributo, pero dichos objetos son la plantilla para los atributos efectivamente insertados en el dibujo, y como tales, no contendrán los valores que a ellos se ha asignado en el momento de la inserción.

El método que permite acceder a los valores de los atributos de una Referencia de Bloque es el método GetAttributes() del objeto AcadBlockReference.

Este método devuelve un objeto de tipo VARIANT que contiene una matriz en la cual cada término apunta a una de las Referencias de Atributo del bloque.

Es decir, que en primer lugar debemos llegar al objeto Referencia de Bloque. Hacerlo recurriendo a la jerarquía de objetos que expone la Interface ActiveX de AutoCAD mediante la programación VBA o Visual Basic exigiría partir desde lo más alto de esta jerarquía, el objeto Aplicación, para desde ahí ir descendiendo hasta llegar a los objetos que nos interesan, todos los cuales forman parte de Colecciones, las Definiciones de Bloque a la colección Blocks, y las Referencias de Bloques y de Atributos a las colecciones ModelSpace o PaperSpace.

Desde Visual LISP, sin embargo, podemos acceder directamente al objeto a partir de su ENAME o nombre de entidad, devuelto por funciones como entsel o desde conjuntos de selección creados con ssget.

Selección de los Bloques a Procesar

Estas funciones AutoLISP exigen muchos menos pasos que las equivalentes ActiveX, por lo que partimos de un conjunto de selección creado de la manera más tradicional, tal como se demuestra en la Figura 2.

Una vez creado el conjunto de selección mediante la función ssget a la que se aportan como filtros la condición de ser una entidad INSERT y el nombre del bloque cuyos datos se desea extraer:

(ssget "X" (list (cons 0 "INSERT")(cons 2 nombre)))

lo recorreremos utilizando como índice la variable cont, inicializada a cero. Para poder utilizar los procedimientos ActiveX, deberemos obtener, a partir del nombre de entidad que se recupera del conjunto de selección mediante la función ssname, una referencia al objeto correspondiente. Esto se logra mediante la función vlax-ename->vla-object. La referencia devuelta por esta función se pasa directamente a la función LeeAtribX que es la encargada de extraer la información deseada y estructurarla en forma de lista.

Figura 2 Rutina que solicita el nombre del Bloque a seleccionar

Extracción de la Información del Bloque

Los objetos ActiveX poseen propiedades, que contienen información equivalente a la que se obtiene a partir de la lista de datos de entidad devuelta por entget. De los datos a exportar, algunos los obtendremos a partir de leer propiedades de la Referencia de Bloque. En primer lugar debemos comprobar si el bloque posee atributos. Este dato corresponde a la propiedad HasAttributes. Esta propiedad puede ser cierta :vlax-true o falsa :vlax-false. Para obtener una propiedad disponemos de la función vlax-get-property. La extracción de datos continuará siempre que se cumpla la condición:

(equal (vlax-get-property objBloque "HasAttributes") :vlax-true)

Los datos que hemos elegido exportar son los siguientes:

·         Nombre del Bloque

·         Identificador y Valor de cada Atributo

·         Coordenadas de la esquina inferior izquierda de la caja de abarque del bloque

·         Coordenadas de la esquina superior derecha de la caja de abarque del bloque

Toda vez que la lista se construirá mediante la sucesiva aplicación de la función cons que añade un primer término a una lista, el orden de extracción de estos datos será al revés de cómo queremos la lista definitiva. Es decir que comenzaremos por las coordenadas de la caja de abarque. La Figura 3 muestra el código de la función que extrae la información y prepara la lista.

Coordenadas de la Caja de Abarque

Aquí se trata ya no de una propiedad, sino de un método que poseen todos los objetos gráficos y que lo heredan del antecesor común AcadObject. Un método no equivale a un dato. Es más bien comparable con una rutina que al recibir determinados parámetros realiza una determinada operación sobre un objeto. Tal operación puede ser la de calcular, como en el caso que nos ocupa, los extremos de su caja de abarque.

El método que hemos de aplicar para ello es el GetBoundingBox ().  Visual LISP proporciona una serie de funciones que sirven como envoltorios para los métodos ActiveX. Estas funciones no las encontraremos en la documentación de Visual LISP. En cambio, el Entorno de Desarrollo (IDE) nos brinda una herramienta que resulta imprescindible en estos casos. Nos referimos a la utilidad APROPOS. Abramos la ventana APROPOS (Ctrl-Shift-A) y tecleemos vla-get en la casilla de edición. Al pulsar OK se abrirá una ventana donde se aprecian los nombres de las 560 funciones disponibles que incluyen vla-get.

Figura 3 Función que extrae datos del Bloque


Figura 4 Resultados de la búsqueda por APROPOS

Si seleccionamos vla-GetBoundingBox, y pulsamos sobre el icono de ayuda en la ventana APROPOS RESULTS accederemos a la Referencia ActiveX y VBA de AutoCAD. Ahí comprobaremos que este método requiere dos argumentos adicionales que no son otros que los nombres de las variables en que deseamos guardar los puntos devueltos.

La sintaxis según la Referencia ActiveX-VBA sería:

object.GetBoundingBox MinPoint, MaxPoint

que traducimos a Visual LISP como:

(vla-GetBoundingBox objBloque 'minimo 'maximo)

Otro paso necesario será la conversión del tipo de dato devuelto al tipo de dato de punto usual en AutoLISP. El tipo devuelto es un VARIANT que contiene una matriz de tres números de coma flotante de doble precisión.

Los puntos aparecerán como cola -cdr- de una lista, cuya cabeza servirá como identificador. En este caso los identificadores los suministramos como una cadena de texto:

(cons "ExtMax" (list (vlax-safearray->list maximo)))

que devolvería algo como:

("ExtMax" (413986.0 4.79686e+006 0.0))

 La necesidad de esta conversión será tan frecuente que Visual LISP suministra una función para ello:

(vlax-safearray->list maximo)

Valores de los Atributos

Otra herramienta que nos permite profundizar en el conocimiento de los objetos ActiveX es INSPECT (Ctrl-Shift-I). Inspect nos permite introducir un nombre de variable que contenga la referencia a un objeto ActiveX, o simplemente seleccionarla con el cursor para abrir una ventana de inspección donde aparecerán realcionadas todas sus propiedades. Proponemos teclear en la consola Visual LISP:

(setq objBloque (vlax-ename->vla-object (car(entsel))))

y seleccionar un bloque en el dibujo. Aplicando la herramienta INSPECT sobre el símbolo objBloque inspeccionaremos sus propiedades. Los atributos se obtendrán mediante el método GetAttributes ()

(setq atributos (vla-GetAttributes objBloque))

La Figura 5 muestra los resultados de aplicar la herramienta INSPECT sobre la variable atributos y los resultados de sucesivos doble clics sobre los conceptos que aparecen en las ventanas resultantes.

Figura 5 Inspección de los objetos ActiveX

El método GetAttributes() devuelve un objeto de tipo VARIANT que contiene una matriz en la cual cada elemento apunta a un objeto Referencia de Atributo.

La recuperación de dichos valores exige una doble transformación.

Los datos de tipo matriz devueltos por los métodos ActiveX están contenidos en VARIANTS. Primero habrá que recuperar el valor del dato desde el tipo VARIANT. Para ello empleamos vlax-variant-value que nos devuelve su contenido que en este caso será una matriz en formato safearray. Entonces utilizaremos vlax-safearray->list que nos devolverá un dato de tipo lista, procesable desde LISP.

Como no sabemos de antemano la cantidad de elementos de que se compone la matriz recurriremos a la función LISP foreach, que es capaz de procesar cada término de la lista, no importa cuántos sean. Cada término de la lista apuntará a un objeto Referencia de Atributo, del que obtendremos los valores deseados mediante la función Procesa (ver Figura 6), que recibe como argumento la referencia a un objeto-VLA que a su vez referencia un Atributo.

Figura 6 Función que recupera los valores de los Atributos

Esta función se limita a crear un par punteado con las cadenas que representan el identificador y el valor del atributo.

Para terminar, se extraerá el nombre del bloque a partir de la propiedad Name del objeto Referencia de Bloque:

(vlax-get-property objBloque "Name")

De la ejecución de este programa resulta una lista que a su vez contiene una sublista para cada bloque. La sublista del bloque a su vez contiene pares de datos estructurados como cabeza y cola de la lista.

((("Bloque" . "FINCA")

   ("PROVINCIA" . "39")

   ("AYUNTAMIENTO" . "087")

   ("PUEBLO" . "0010")

   ("CALLE" . "0190")

   ("PORTAL" . "0012")

   ("PORT-BIS" . "51")

   ("ESCALERA" . "00")

   ("ALTURAS" . "0001")

   ("ExtMin" (413981.0 4.79685e+006 0.0))

   ("ExtMax" (413986.0 4.79686e+006 0.0))

))

Conclusiones

Gran parte de la información que contiene esta lista pudiera haber sido extraída a partir de una función puramente LISP como la que se muestra en la Figura 1. Sin embargo, la información que se refiere a la caja de abarque hubiera debido ser obtenida mediante una función programada a partir de cero, y de una manera que tuviera en cuenta los diversos objetos gráficos a los que se pudiera aplicar.

En un entorno de Programación Orientada a objetos, sin embargo, un método como el GetBoundingBox () es heredado por todos los objetos gráficos a partir de su antecesor común AcadObject lo que nos permite obtener este dato de manera inmediata. Y esto sin renunciar a las ventajas de la programación LISP. Digamos que "lo mejor de ambos mundos".

Para terminar, los lectores de nuestro anterior artículo observarán que el formato de la lista resultante es exactamente la que requiere la función Lista->Excel allí reseñada. De manera que con esto completaríamos un programa capaz de extraer información de los bloques de un dibujo de AutoCAD para exportarlos a una tabla de Excel.