4.2. Atributos como objetos ActiveX

Nuestro artículo anteriorrefería la manera en que Visual LISP es capaz de gestionar otras aplicacionesdel entorno Windows. Continuamos ahora demostrando cómo es capaz también deacceder a la propia jerarquía de objetos de AutoCAD, gestionando sus métodos ypropiedades.


El trabajar con bloques aporta eficacia al uso de unsistema 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 referenciadesde cada uno de sus inserciones en el dibujo. Es decir que lo que aparece encada ocurrencia del bloque es como una vista del paquete original,desplazada al lugar de la inserción, posiblemente girada y en algunos casossometida a transformaciones de escala.

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

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

El segundo es una copia geométricamente transformadadel primero.

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

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

Para dar solución a este caso se recurre a un procesosimilar al utilizado para generar el bloque mismo: se crea una entidad gráficaespecial, 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 elmomento de generarse cada ocurrencia particular, es decir en el momento de lainserción del bloque, pasando a generar una entidad nueva, el objeto  Referenciade Atributo, asociado siempre al objeto Referencia de Bloque. La Referenciade Atributo se asocia a la Referencia de Bloque estableciendo unasecuencia en la base de datos de entidades, de manera tal que este últimoobjeto actúa como cabecera de una sucesión de Referencias de Atributoque viene a concluir con la entidad especial Fin de Secuencia. Elcomando DBLIST aplicado en undibujo con bloques insertados permite comprobar este aspecto.

Acceso a los Atributos

El mecanismo habitual para recuperar los atributos desdeAutoLISP ha sido el de recorrer linealmente la base de datos gráfica, partiendode la entidad INSERT (Referenciade Bloque) mediante la función entnexthasta encontrar la entidad SEQEND(Fin de Secuencia). La Figura 1 muestra, a manera de ejemplo, el códigode una función de este tipo.

Figura 1 Función recursiva LISP para extraer Atributos

El entorno ActiveX exige proceder dedistinta manera. Los objetos Referencia de Atributo se han de recuperarmediante un método especializado del objeto Referencia de Bloque.Al recorrer las colecciones Blocks, ModelSpace o PaperSpace, pueden aparecerobjetos Definición de Atributo, pero dichos objetos son la plantillapara los atributos efectivamente insertados en el dibujo, y como tales, nocontendrán los valores que a ellos se ha asignado en el momento de lainserción.

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

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

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

Desde Visual LISP, sin embargo, podemos acceder directamenteal objeto a partir de su ENAME onombre 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 lasequivalentes ActiveX, por lo que partimos de un conjunto de selección creado dela manera más tradicional, tal como se demuestra en la Figura 2.

Una vez creado el conjunto de selecciónmediante la función ssget a la que se aportan como filtros la condición de ser una entidadINSERT 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 poderutilizar los procedimientos ActiveX, deberemos obtener, a partir del nombre deentidad 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. Lareferencia devuelta por esta función se pasa directamente a la función LeeAtribX que es la encargada deextraer la información deseada y estructurarla en forma de lista.

Figura 2 Rutina que solicita el nombre del Bloque aseleccionar

Extracción de la Información del Bloque

Los objetos ActiveX poseen propiedades, que contieneninformación equivalente a la que se obtiene a partir de la lista de datos deentidad devuelta por entget. Delos datos a exportar, algunos los obtendremos a partir de leer propiedades dela 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-trueo falsa :vlax-false. Paraobtener 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 deabarque del bloque

·        Coordenadas de la esquina superior derecha de la caja de abarquedel bloque

Toda vez que la lista se construirá mediante la sucesivaaplicación de la función consque añade un primer término a una lista, el orden de extracción de estos datosserá al revés de cómo queremos la lista definitiva. Es decir que comenzaremospor las coordenadas de la caja de abarque. La Figura 3 muestra el códigode 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étodoque 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 alrecibir determinados parámetros realiza una determinada operación sobre unobjeto. 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 LISPproporciona una serie de funciones que sirven como envoltorios para losmétodos ActiveX. Estas funciones no las encontraremos en la documentación deVisual LISP. En cambio, el Entorno de Desarrollo (IDE) nos brinda unaherramienta que resulta imprescindible en estos casos. Nos referimos a lautilidad APROPOS. Abramos la ventana APROPOS (Ctrl-Shift-A) y tecleemos vla-get en la casilla de edición. Alpulsar OK se abrirá una ventana donde se aprecian los nombres de las 560funciones 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 queVisual LISP suministra una función para ello:

(vlax-safearray->listmaximo)

Valores de los Atributos

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

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

y seleccionar un bloque en el dibujo. Aplicando laherramienta 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 herramientaINSPECT sobre la variable atributosy los resultados de sucesivos doble clics sobre los conceptos que aparecen enlas ventanas resultantes.

Figura 5 Inspección de los objetos ActiveX

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

La recuperación de dichos valores exigeuna doble transformación.

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

Como no sabemos de antemano la cantidadde elementos de que se compone la matriz recurriremos a la función LISP foreach, que escapaz de procesar cada término de la lista, no importa cuántos sean. Cadatérmino de la lista apuntará a un objeto Referencia de Atributo, del queobtendremos los valores deseados mediante la función Procesa (ver Figura6), que recibe como argumento la referencia a un objeto-VLA que a su vezreferencia un Atributo.

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

Esta función se limita a crear un parpunteado con las cadenas que representan el identificador y el valor delatributo.

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

(vlax-get-propertyobjBloque "Name")

De la ejecución de este programa resulta una lista que a suvez contiene una sublista para cada bloque. La sublista del bloque a su vezcontiene 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.04.79685e+006 0.0))

   ("ExtMax" (413986.04.79686e+006 0.0))

))

Conclusiones

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

En un entorno de Programación Orientada a objetos, sinembargo, un método como el GetBoundingBox() es heredado por todos los objetos gráficos a partir de su antecesorcomún AcadObject lo que nospermite obtener este dato de manera inmediata. Y esto sin renunciar a lasventajas de la programación LISP. Digamos que "lo mejor de ambosmundos".

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