4.3. Juntándolo todo... y una pizca de DCL

Los dos artículos anterioresexaminaban la capacidad de Visual LISP para acceder a las propiedades y métodosde los objetos ActiveX. Ahora utilizaremos las funciones en ellos desarrolladas(CadXpress 61 y 62) para crear un programa Visual LISP dotado de una interfazgráfica de usuario programada en DCL.


Antecedentes

En el número 61 describíamos la manera de acceder a una hojade cálculo EXCEL desde AutoCAD y llenar los campos de una tabla con los datosde una lista de asociación LISP.

En el siguiente número examinamos una función capaz deextraer la información de los atributos y otros datos asociados a lasreferencias de bloque.

Contamos con que el lector ya dispondrá de las siguientesfunciones organizadas en dos archivos:

Achivo fuente:

Funciones:

Propósito:

ListaExcel.LSP

apl-err

Gestión de errores.

IniciaExcel

Establece la comunicación.

TerminaExcel

Libera la aplicación.

DatoCelda

Inserta una valor en una celda Excel.

ProcesaFila

Escribe los campos de una fila.

ProcesaTabla

Escribe la fila de títulos y llena la tabla.

Lista->Excel

Recibe la lista e invoca las funciones anteriores

LeeAttribX.LSP
(CadXpress 62)

LeeAtribX

Extrae datos a partir de un objeto Referencia de Bloque

Procesa

Lee el identificador y el valor de un atributo

Los artículos mencionados incluyen además funcionesdiseñadas con el objeto de probar el funcionamiento de los programas. De una deesas funciones podemos ahora prescindir, la función TablaCirculos. Otra la utilizaremos con pequeñasmodificaciones:

Artículo:

Funciones:

Propósito:

CadXpress 61

TablaCirculos

No se utilizará

C:TABLA

No se utilizará

CadXpress 62

SelBloque

Se modifica para que reciba el nombre de un bloque seleccionado de un Cuadro de Diálogo en lugar de teclearlo el usuario

A estos dos archivos añadiremos en esta entrega el archivocon la definición del Cuadro de Diálogo y otro con las funciones Visual LISPnecesarias para gestionarlo y que explicaremos más abajo:

 

Achivo fuente:

Funciones:

Propósito:

ExtrATT.LSP


CargaDialogo

Inicia la operación del Cuadro de Diálogo.

CompruebaAtributos

Comprueba que el bloque seleccionado posea atributos.

ExtrATT

Lanza la función Lista->Excel.

ListaNombres

Devuelve una lista con los bloques de usuario encontrados en el dibujo.

TieneAtributos

Discrimina si una Definición de Bloque incluye atributos.

SelBloque

Selecciona las Referencias de Bloque que corresponden al nombre de bloque seleccionado por el usuario.

ExtrATT.DCL

 

Definición del Cuadro de Diálogo

El Cuadro de Diálogo

El cuadro de diálogo propuesto (ver Figura 1) es muy simple,aunque sirve perfectamente a los fines que nos proponemos.

 Figura 1. Cuadro de Diálogo de la Aplicación.

 El componente principal del Cuadro de diálogo lo es unacasilla de lista (list_box) quepresenta los nombres de los bloques presentes en el dibujo. Para cada nombre debloque se indica si tiene atributos insertando el texto "ATRIB" en la fila correspondiente. Elordenamiento de este texto en forma de columna se logra introduciendo uncarácter tabulador "\t"entre el nombre del bloque y el texto "ATRIB",y asignando al atributo tabs dela casilla de lista un valor de 33,siendo la anchura total (width)de  40. Esta casilla de listasólo admite la selección de un nombre de bloque (multiple_select= false;).

Los otros componentes del diálogo son los botones de Aceptary Cancelar que provienen del componente predefinido ok_cancel y una línea de texto para indicar errorestambién predefinida a partir del componente errtile.Para el código completo del archivo ExtrATT.DCLvéase la Figura 2. Es importante que la ubicación de este archivo se encuentreen una carpeta que figure dentro de las rutas de búsqueda de archivos desoporte de AutoCAD. Estas rutas pueden definirse desde el cuadro de diálogo Opciones...del menú Herramientas. En caso de duda, puede simplemente ubicarse en lacarpeta SUPPORT de AutoCAD.

Figura 2.  Código de la Definición del Cuadro de diálogo.

 Gestión del Cuadro de Diálogo

La operación de Cuadro de Diálogo se asegura a partir de lafunción CargaDialogo (ver Figura3). Lo primero que comprueba esta función es la existencia de una variableglobal que llamamos *Posicion*.En caso que no exista se valora como '(-1-1). Esta variable se suministra a la función new_dialog y sirve para determinar la posición deldiálogo en la pantalla. Si el usuario desplazara el Cuadro a una nuevaposición, los valores que a ella corresponden serán devueltos al cerrarse eldiálogo mediante done_dialog yel programa almacenaría las nuevas coordenadas, de manera que al volverlo ainvocar reaparecería en la última posición que ocupaba.

Una vez valorada *Posicion*,el programa comprobará que el archivo DCL está disponible y que la definicióndel Cuadro de Diálogo puede ser cargada, alertando de cualquier errorencontrado. De aparecer uno de estos mensajes, debemos asegurarnos de habersituado el archivo DCL en la carpeta adecuada y de que no contiene erroressintácticos.

Llenar la Casilla de Lista

En caso de que todo marche bien, se procederá a llenar lacasilla de lista con los nombres de los bloques encontrados en el dibujo. Paraobtener estos nombres se llama a la función ListaNombres,que reviste cierta complejidad y que será explicada más abajo. Sólo diremosahora que los nombres se guardan en una lista de asociación compuesta por parespunteados[1].

El procedimiento para llenar una lista DCL es el habitual:

·        Una llamada a la función start_lista la que se suministra la clave (key)asignada al componente:

(start_list"lista_bloques")

·        Una invocación de la función mapcarque aplica la función 'add_lista cada miembro de la lista.
Aquí tenemos una variante, otra llamada a mapcaranidada en la anterior, que tiene como objetivo crear una cadena queconvertirá el par punteado

("NOMBREBLOQUE" ."ATRIB") en la cadena "NOMBREBLOQUE\tATRIB".

 

Figura 3. Función CargaDialogo

Esto se logra aplicando unafunción lambda, es decir unafunción anónima, definida en tiempo de ejecución y que desaparece una vezcumplido su objetivo:

(mapcar 'add_list

  (mapcar '(lambda(term) (strcat (car term) "\t" (cdr term)))

  *listaBloques*))

Si ListaNombresdevolviera una lista vacía en lugar de llenar la casilla de lista sepresentaría un mensaje de error en el componente errtile:

(set_tile"error" "No hay bloques en el Dibujo Actual")

·        El proceso de escritura en la lista concluye con la llamada a end_list.

Estableciendo las acciones de los componentes

Una vez llena la casilla de lista se procede a asignar laacción que desencadenará cada componente del Diálogo. La casilla de listatendrá una acción propia asignada mediante action_tile:

(action_tile"lista_bloques" "(CompruebaAtributos $value)")

El argumento $valueque se pasa a la función CompruebaAtributos(ver Figura 4) contendrá el índice de la línea sobre la que se haya pulsado elratón, bien entendido que a la primera línea corresponderá el valor cero. Lagestión de la casilla de lista exige conservar en memoria la lista original *listaBloques*. Para comprobar elbloque a que corresponde la línea pulsada se extraerá el miembro de la listaque ocupa la posición que corresponde al índice devuelto por $value. CompruebaAtributoslo hace aplicando la función nth:

(nth (atoi valor)*listaBloques*)

El valor devuelto que es de tipo String por lo que debe convertirse en Integer mediante la función atoi.

Se revisa entonces si el segundo término del par punteado esigual a "ATRIB". Encaso contrario, se muestra un mensaje en errtileindicando que "El bloqueseleccionado no posee atributos"

Figura 4. Función CompruebaAtributos.

Debemos señalar que además de esta acción que corresponde alevento de un clic del ratón, esta casilla tendrá también asignada laacción del botón Aceptar que se lanzará a partir del doble clicsobre una de sus líneas, tal como se define en el código DCL:  allow_accept = true;

La acción para la tecla Aceptar se define en lafunción ExtrATT a la que se pasacomo argumento el índice de la línea resaltada, que se obtiene esta vezmediante la función get_tile:

(action_tile"accept" "(ExtrATT (get_tile\"lista_bloques\"))")

Figura 5. Función ExtrATT

La función ExtrATT(ver Figura 5) comprobará ante todo que exista *listaBloques*,previendo que el dibujo pueda no contener bloque alguno. Después comprueba queel segundo término del par punteado que identifica el bloque sea "ATRIB". Si ambasverificaciones tienen éxito, se lanza la función Lista->Excel(CadXpress 61) que recibe como argumento la función SelBloque (CadXpress 62), esta última modificada paraeliminar la solicitud al usuario del nombre del bloque, ya que en este caso sele pasaría a partir de una selección en el cuadro de diálogo. (ver Figura 6).

El botón Cancelar no tiene otra acción asignada queregistrar la posición del cuadro de diálogo al cerrarse.

Figura 6. Función SelBloque.

Creando la lista de Bloques

Como hemos visto, toda la funcionalidad de este Diálogo giraen torno a la lista de nombres de bloques *listaBloques*.Para acceder a esta información seguiremos la línea marcada en los artículosanteriores, empleando los métodos y propiedades de los objetos ActiveX. Paraello debemos conocer algo de la estructura de AutoCAD como aplicación.

AutoCAD en sí mismo constituye un objeto, el objetoAplicación. Es a partir de este objeto que se podrá llegar a todos loscomponentes que integran el dibujo. Los objetos que se incluyen en laaplicación se cuentan por centenares. De ahí que sea necesario estructurarlosde alguna manera. Esa manera de estructurarlos es a partir de Colecciones.Todos los dibujos abiertos en una misma sesión de trabajo componen la ColecciónDocumentos. El Documento Activo es uno de los miembros de esaColección. A su vez el Documento Activo contiene una serie de Colecciones deObjetos.

Las Colecciones de Objetos

Una colección se define comoun conjunto ordenado de elementos de información sobre los que podemos operarcomo una estructura de carácter unitario. Desde el punto de vista delprogramador AutoLISP, una colección es en gran medida como una lista. Aunquelas colecciones no tienen que contener necesariamente el mismo tipo de datopara cada uno de sus miembros, las colecciones de AutoCAD tienden a componersede referencias a objetos de tipo similar. Por ejemplo, la Colección "Blocks" contiene sólo objetos Bloque y la Colección"TextStyles" contiene sólo objetos Estilo de Texto[2].

La vía para obtener la colección "BLOCKS" será lasiguiente:

·        Obtener el objeto Aplicación AutoCAD

·        Obtener su objeto Documento Activo

·        Obtener del Documento Activo la Colección "BLOCKS"

·        Extraer los nombres de los objetos que integran lacolección.

Los dos primeros pasos se resumen en la siguiente línea decódigo:

(setq*EsteDibujo* (vla-get-ActiveDocument (vlax-get-acad-object)))

Esta línea de código no forma parte de una de las funcionesdefinidas. Aparecen de manera independiente al principio del archivo ExtrATT.LSP (ver Figura 7) junto conla llamada a (vl-load-com)[3],con lo que se valorará de inmediato en el momento de cargar el programa.

Figura 7. Funciones de Inicialización.

La referencia al objeto Documento Activo se guarda en lavariable global *EsteDibujo*. Lafunción ListaNombres utilizaesta referencia para acceder a la Colección "Blocks"(ver Figura 8). La colección "Blocks"es una propiedad del objeto Documento recuperable mediante

(vlax-get-property*EsteDibujo* "Blocks")

Visual LISP suministra vlax-for,una función que nos permite iterar a través de una colección con la mismafacilidad que lo haríamos con una lista:

(vlax-forobj (vlax-get-property *EsteDibujo* "Blocks")...

De cada uno de los objetos que componen la colección y querecuperamos secuencialmente, podemos obtener la propiedad "Name", es decir su nombre:

(vlax-get-propertyobj "Name")

Figura 8. Función ListaNombres.

Pero no todos los objetos recuperados a partir de lacolección "Blocks"interesan a los efectos de nuestro programa. Sólo deseamos obtener los nombresde bloques definidos por el usuario. Hay toda una serie de posiblesobjetos de tipo bloque que debemos excluir: los llamados bloques anónimos,generados y gestionados por AutoCAD que se distinguen por poseer como primercarácter un asterisco "*",los bloques de usuario que dependen de Referencias Externas y que se conocenpor incluir un carácter pipe (|),y las Referencias Externas en sí mismas (detectados a partir de supropiedad "IsXRef").De ahí la condicional en que se ubica el código para confeccionar la lista debloques. Para las dos primeros casos se utiliza la función wcmatch con la cadena "`**,*|*" como patrón decomparación.

Otro aspecto que nos interesa comprobar ahora es si lasDefiniciones de Bloque restantes incluyen Definiciones de Atributos[4].Para ello será necesario examinar, una a una, las Definiciones de Bloqueencontradas. Una Definición de Bloque es también una Colección. En estecaso integrada por entidades entre las cuales pueden encontrarse Definicionesde Atributo. La función TieneAtributos(ver figura 9) recorre mediante vlax-forcada Definición de Bloque y devuelve t(cierto) en caso de que la propiedad "ObjectName"de alguna entidad sea "AcDbAttributeDefinition".

Figura 9. Función TieneAtributos.

Implantar un nuevo Comando AutoCAD

Para terminar, sería conveniente crear un nombre para esteprograma que permitiera llamarlo desde la línea de comandos. En AutoLISP lohacíamos definiendo una función cuyo nombre estuviera precedido por"C:". Ahora disponemos de otras maneras para hacerlo. Agregamos alfinal del archivo ExtrATT.LSP(ver figura 3, abajo) la siguiente línea:

(vlax-add-cmd"AtrEX" 'CargaDialogo)

Ahora bastará teclear ATREXen la línea de comandos para acceder al programa.

Recordemos que para probarlo debemos cargar todos losarchivos que componen el proyecto:ListaExcel.LSP, LeeAttribX.LSP y ExtrATT.LSP.

Conclusiones

Con lo publicado en este número completamos lo que sería elcódigo para una aplicación de utilidad evidente. Más adelante abordaremos losprocedimientos para crear un proyecto que nos ayude a gestionar los diversosarchivos, los aspectos a tener en cuenta para su compilación y el procesomediante el cual podemos convertir todo esto en una aplicación ejecutable VLX.


[1] Listas de dos términos deltipo que se obtiene al aplicar la función cons sobre dos átomos: (cons "A" "B"), yque devuelve ("A" ."B"). Los pares punteados, también conocidos como CONSES sediferencian de las verdaderas listas en una serie de aspectos, entreellos que la función cdraplicada a un par punteado no devuelve una lista, sino un átomo. (cdr '("A" . "B"))devolvería "B",mientras que (cdr ("A""B")) devolverá ("B").

[2] Gibb,J.W. y Kramer, B. AutoCAD VBA Programming: tools and techniques. Miller-Freeman,1999. Pág. 83. Traducción libre de R. Togores.

[3] Sobre vl-load-com ver CadXpress 60.

[4] Recordemos la distinciónentre Definición de Atributo y Referencia de Atributo que hacíamos en nuestroartículo anterior (CadXpress 62).