Autolisp no establece correctamente el atributo de bloque para algunos usuarios
Pregunta por primera vez, así que espero estar describiendo el problema lo suficientemente bien.
Tenemos un código de Autolisp en nuestra empresa que es utilizado por varias personas con la misma versión de AutoCAD, pero para algunos de los usuarios el lisp ha dejado de funcionar correctamente.
La función del lisp es la siguiente:
- el usuario ejecuta el lisp
- el programa pide lo siguiente:
- escala del bloque
- prefijo para texto en bloque
- el número de ejecución para la primera entidad de bloque
- el incremento del número corriente
- donde colocar el primer bloque
Esto debería conducir a un bloque con un marcador y un texto con el siguiente formato (prefijo) (posible sección central si el número no consta de tres números) (el número continuo), por ejemplo, PT001 o PX100.
Sin embargo, en lugar de hacer esto, algunos de los usuarios han experimentado la falta de prefijo y número y comenzaron a ver solo la posible sección central del texto mencionada anteriormente, mientras que otras veces el mismo usuario puede experimentar que solo se muestra el prefijo. El marcador se muestra como debería ser, pero el texto simplemente no funcionará como se esperaba.
Se agradece enormemente cualquier ayuda para analizar el código siguiente en busca de defectos.
Si el código parece ser "impecable", supongo que hay un problema con el bloque o sus atributos.
-MI
(defun c:pointnumber()
(setvar "ATTDIA" 0)
(setq sc (getreal "\nEnter scale: "))
(setq px (getstring "\nSet prefix for point number: "))
(setq nr (getint "\nThe number for the first point: "))
(setq ic (getint "\nIncrement of the number: "))
(setq point 1)
(while (/= point nil)
(setq point (getpoint "\nChoose a point: "))
(if (/= point nil)
(progn
(setq inr (itoa nr))
(if (< nr 100) (setq md "0"))
(if (< nr 10) (setq md "00"))
(if (> nr 99) (setq md ""))
(setq ph (strcat px md inr))
(command "insert" "pointnumber" point sc sc 0 ph)
(setq nr (+ nr ic))
)
)
)
(setvar "ATTDIA" 1)(princ)
)
Respuestas
Hay una serie de problemas con su código actual: algunos de los cuales podrían simplemente considerarse una mala práctica, algunos harán que el programa falle si el usuario responde con datos no válidos y otros harán que el programa falle o se comporte de manera inesperada dependiendo de la configuración del entorno de AutoCAD en el que se ejecuta el programa.
1. ATTREQ
Es probable que el principal culpable del comportamiento que ha descrito sea la ATTREQvariable del sistema, que determina si el usuario recibirá solicitudes de valores de atributo como parte del INSERTcomando. Si ATTREQ=0
cuando se ejecuta el programa, el bloque se inserta con sus valores de atributo predeterminados.
Puede garantizar un comportamiento coherente entre entornos almacenando el valor actual de esta variable del sistema y configurándolo en 1
antes de llamar al INSERTcomando (para asegurarse de que se emitan las solicitudes de atributos), y luego restaurando el valor original después del comando o al final del programa.
Por ejemplo:
(defun c:test ( / atr )
(setq atr (getvar 'attreq))
(setvar 'attreq 1)
;; ... Do your thing
(setvar 'attreq atr)
(princ)
)
2. OSMODE
Al proporcionar datos de puntos a un comando a través de AutoLISP, el punto se verá afectado por cualquier modo de referencia a objetos activo en el momento en que se proporciona el punto. Describo esto con más detalle en mi respuesta aquí .
La forma más fácil de evitar esto es mediante el uso del modificador de referencia a objetos "_non"
o "_none"
para indicar a AutoCAD que ignore todos los modos de referencia a objetos para la entrada de puntos posterior, por ejemplo:
(command "insert" "pointnumber" "_non" point sc sc 0 ph)
3. Entrada del usuario
Debe tener en cuenta la falta de entrada de usuario o entrada de usuario no válida para evitar errores durante la ejecución del programa; esto se logra fácilmente mediante el uso de if
declaraciones o la initget
función, por ejemplo:
(initget 7) ;; Prevents Enter, zero, or negative numbers
(setq sc (getreal "\nEnter scale: "))
O:
(initget 6)
(if
(and
(setq sc (getreal "\nEnter scale: "))
(setq px (getstring "\nSet prefix for point number: "))
(setq nr (getint "\nThe number for the first point: "))
(setq ic (getint "\nIncrement of the number: "))
)
;; ... Do your thing
)
Alternativamente, puede configurar valores predeterminados para cada una de estas solicitudes, utilizando uno de los métodos que describo en mi tutorial sobre Solicitar con una opción predeterminada , por ejemplo:
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0)))
4. Variables locales y globales
Actualmente, todas las variables de su programa son variables globales : es decir, están definidas dentro del espacio de nombres del documento (dibujo) y conservarán sus valores incluso después de que el programa haya completado su ejecución.
Como describo en mi tutorial sobre la localización de variables , esto puede potencialmente causar problemas si tales variables comparten inadvertidamente sus nombres con variables globales utilizadas por otros programas, o cuando un programa está construyendo una lista u otra estructura de datos acumulativos dentro de un bucle.
A menos que el uso de una variable global sea necesario para el correcto funcionamiento del programa, sugeriría declarar esas variables locales a la función, por ejemplo:
(defun c:pointnumber ( / ic inr md nr ph point px sc ) ;; Local variables
;; ...
)
5. Comprobación de la existencia del bloque
Proporcionar el nombre del bloque directamente al INSERTcomando supone que ya existe una definición de ese bloque dentro del dibujo activo, o que existe un dibujo con ese nombre de archivo dentro del directorio de trabajo o en una ruta de búsqueda de archivos de soporte de AutoCAD; si no se cumple ninguna de las condiciones, el INSERTcomando producirá un error durante la ejecución del programa.
Por lo tanto, puede probar estas condiciones de antemano, notificando al usuario si el bloque no se encuentra, de lo contrario, proceda a ejecutar el resto de las operaciones:
(if
(or
(tblsearch "block" "pointnumber") ;; Checks for existing definition
(findfile "pointnumber.dwg") ;; Checks for drawing file
)
;; ...
)
También puede utilizar la cond
función en lugar de una secuencia de if/else
expresiones.
6. Restablecimiento del entorno en caso de error
Dado que está cambiando los valores de las variables del sistema durante la ejecución del programa, debe asegurarse de que el entorno de AutoCAD del usuario se restablezca a su estado original en caso de un error durante la ejecución del programa, teniendo en cuenta que el usuario que presione Escpara salir del programa también resultará en un error. error.
Puede lograr esto definiendo un controlador de errores local, como describo en mi tutorial sobre Manejo de errores . La función de error local se evalúa si se encuentra un error durante la ejecución del programa, por lo que puede incluir expresiones dentro de la definición de esta función para restablecer el entorno de AutoCAD a su estado original; en su caso, esto implicaría restablecer el valor original del ATTDIA
variable de sistema.
Poniendolo todo junto
;; Define function, declare local variables
(defun c:pointnumber ( / *error* bn ic nr ns pt px sc vl vr )
;; Define local error function to reset system variables on error
(defun *error* ( msg )
(mapcar 'setvar vr vl) ;; Reset list of system variables
(if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
(princ (strcat "\nError: " msg))
) ;; end if
(princ)
) ;; end defun
;; Define block name
(setq bn "pointnumber")
(if (or (tblsearch "block" bn) ;; Definition in drawing
(findfile (strcat bn ".dwg")) ;; Drawing file in support path
) ;; end or
(progn
(initget 6) ;; Prevents 0 & negatives
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0))
px (getstring "\nSpecify prefix <none>: ")
) ;; end setq
(initget 4) ;; Prevents negatives
(setq nr (cond ((getint "\nSpecify starting number <1>: ")) (1)))
(initget 6) ;; Prevents 0 & negatives
(setq ic (cond ((getint "\nSpecify increment <1>: ")) (1)))
(setq vr '(attreq attdia cmdecho) ;; List of system variables
vl (mapcar 'getvar vr) ;; Store current values
) ;; end setq
(mapcar 'setvar vr '(1 0 0)) ;; Set system variables appropriately
(while (setq pt (getpoint "\nSpecify point <exit>: "))
(setq ns (itoa nr)
nr (+ nr ic)
)
(repeat (- 3 (strlen ns)) (setq ns (strcat "0" ns))) ;; Pad to 3 digits
(command "_.-insert" bn "_S" sc "_R" "0" "_non" pt (strcat px ns))
) ;; end while
(mapcar 'setvar vr vl) ;; Reset list of system variables to their original values
) ;; end progn
;; Else the block was not defined/found
(princ (strcat "\nThe block \"" bn "\" is not defined in the active drawing and cannot be found."))
) ;; end if
(princ) ;; Suppress the value returned by the last evaluated expression
) ;; end defun
Hay otras posibles mejoras que también podrían implementarse, como:
Eliminar la dependencia de las llamadas a los comandos estándar de AutoCAD (en este caso, el INSERTcomando) mediante el uso del
insertblock
método ActiveX o las funcionesentmake
/entmakex
para escribir datos DXF directamente en la base de datos de dibujo.Completar atributos haciendo referencia a sus nombres de etiqueta de atributo para eliminar la dependencia del orden en el que se encuentran las referencias de atributo dentro de la referencia de bloque (que podría modificarse por dibujo mediante el uso del BATTMANcomando).
Usar un 'valor predeterminado dinámico' (como se describe en mi tutorial ) y potencialmente almacenar el valor de los valores predeterminados entre sesiones de dibujo.