Autolisp ne définit pas correctement l'attribut de bloc pour certains utilisateurs
Demandeur pour la première fois, alors j'espère que je décris assez bien le problème.
Nous avons un code Autolisp dans notre entreprise qui est utilisé par plusieurs personnes avec la même version d'AutoCAD, mais pour certains utilisateurs, le lisp a cessé de fonctionner correctement.
La fonction du lisp est la suivante:
- l'utilisateur exécute le lisp
- le programme demande les choses suivantes:
- échelle du bloc
- préfixe pour le texte dans le bloc
- le numéro courant de la première entité de bloc
- l'incrément du nombre courant
- où placer le premier bloc
Cela devrait conduire à un bloc avec un marqueur et un texte avec le format suivant (préfixe) (section centrale possible si le numéro ne se compose pas de trois chiffres) (le numéro courant), par exemple PT001 ou PX100.
Au lieu de faire cela, cependant, certains utilisateurs ont connu le manque de préfixe et de numéro et ont commencé à ne voir que la section centrale possible susmentionnée du texte, tandis que d'autres fois, le même utilisateur peut constater que seul le préfixe est affiché. Le marqueur est affiché comme il se doit, mais le texte ne fonctionnera tout simplement pas comme prévu.
Toute aide pour analyser le code ci-dessous pour les failles est grandement appréciée.
Si le code semble "parfait", je suppose qu'il y a un problème avec le bloc ou ses attributs.
-E
(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)
)
Réponses
Il existe un certain nombre de problèmes avec votre code actuel: certains peuvent simplement être considérés comme une mauvaise pratique, certains entraîneront l'échec du programme si l'utilisateur répond avec des données non valides, et d'autres entraîneront l'échec du programme ou un comportement inattendu en fonction de la les paramètres de l'environnement AutoCAD dans lequel le programme est exécuté.
1. ATTREQ
Le principal responsable du comportement que vous avez décrit est probablement la ATTREQvariable système, qui détermine si l'utilisateur recevra des invites pour les valeurs d'attribut dans le cadre de la INSERTcommande. Si ATTREQ=0
lors de l'exécution du programme, le bloc serait inséré avec ses valeurs d'attribut par défaut.
Vous pouvez garantir un comportement cohérent entre les environnements en stockant la valeur actuelle de cette variable système et en la définissant sur 1
avant d'appeler la INSERTcommande (pour vous assurer que les invites d'attribut sont émises), puis en restaurant la valeur d'origine après la commande ou à la fin de la commande. programme.
Par example:
(defun c:test ( / atr )
(setq atr (getvar 'attreq))
(setvar 'attreq 1)
;; ... Do your thing
(setvar 'attreq atr)
(princ)
)
2. OSMODE
Lors de la fourniture de données de point à une commande via AutoLISP, le point sera affecté par tous les modes d'accrochage aux objets actifs au moment où le point est fourni. Je décris cela plus en détail dans ma réponse ici .
Le moyen le plus simple d'éviter cela consiste à utiliser le modificateur d'accrochage aux objets "_non"
ou "_none"
pour indiquer à AutoCAD d'ignorer tous les modes d'accrochage aux objets pour l'entrée de point suivante, par exemple:
(command "insert" "pointnumber" "_non" point sc sc 0 ph)
3. Entrée utilisateur
Vous devez tenir compte d'un manque d'entrée utilisateur ou d'une entrée utilisateur invalide pour éviter les erreurs lors de l'exécution du programme - cela est facilement réalisé soit par l'utilisation d' if
instructions ou de la initget
fonction, par exemple:
(initget 7) ;; Prevents Enter, zero, or negative numbers
(setq sc (getreal "\nEnter scale: "))
Ou alors:
(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
)
Vous pouvez également configurer les valeurs par défaut pour chacune de ces invites, en utilisant l'une des méthodes que je décris dans mon didacticiel sur les invites avec une option par défaut , par exemple:
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0)))
4. Variables locales et globales
Actuellement, toutes les variables de votre programme sont des variables globales : c'est-à-dire qu'elles sont définies dans l'espace de noms du document (dessin) et conserveront leurs valeurs même après que le programme ait terminé son exécution.
Comme je le décris dans mon tutoriel sur la localisation des variables , cela peut potentiellement causer des problèmes si ces variables partagent par inadvertance leurs noms avec des variables globales utilisées par d'autres programmes, ou lorsqu'un programme construit une liste ou une autre structure de données acculumative dans une boucle.
À moins que l'utilisation d'une variable globale ne soit nécessaire pour le bon fonctionnement du programme, je suggérerais de déclarer ces variables locales à la fonction, par exemple:
(defun c:pointnumber ( / ic inr md nr ph point px sc ) ;; Local variables
;; ...
)
5. Vérification de l'existence d'un bloc
La fourniture du nom de bloc directement à la INSERTcommande suppose que soit une définition de ce bloc existe déjà dans le dessin actif, soit qu'un dessin portant ce nom de fichier existe soit dans le répertoire de travail, soit dans un chemin de recherche de fichier de support AutoCAD - si aucune des conditions n'est remplie, la INSERTcommande provoquera une erreur lors de l'exécution du programme.
Vous pouvez donc tester ces conditions au préalable, en avertissant l'utilisateur si le bloc n'est pas trouvé, sinon en procédant à l'exécution du reste des opérations:
(if
(or
(tblsearch "block" "pointnumber") ;; Checks for existing definition
(findfile "pointnumber.dwg") ;; Checks for drawing file
)
;; ...
)
Vous pouvez également utiliser la cond
fonction à la place d'une séquence d' if/else
expressions.
6. Réinitialisation de l'environnement en cas d'erreur
Étant donné que vous modifiez les valeurs des variables système pendant l'exécution du programme, vous devez vous assurer que l'environnement AutoCAD de l'utilisateur est réinitialisé à son état d'origine en cas d'erreur lors de l'exécution du programme - en notant que l'utilisateur qui appuie sur Escpour quitter le programme entraînera également un Erreur.
Vous pouvez accomplir cela en définissant un gestionnaire d'erreurs local, comme je le décris dans mon didacticiel sur la gestion des erreurs . La fonction d'erreur locale est évaluée si une erreur est rencontrée pendant l'exécution du programme, et vous pouvez donc inclure des expressions dans la définition de cette fonction pour réinitialiser l'environnement AutoCAD à son état d'origine - dans votre cas, cela impliquerait de réinitialiser la valeur d'origine du ATTDIA
variable système.
Mettre tous ensemble
;; 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
Il existe d'autres améliorations possibles qui pourraient également être mises en œuvre, telles que:
Suppression des appels aux commandes AutoCAD standard (dans ce cas, la INSERTcommande) via l'utilisation de la
insertblock
méthode ActiveX ou des fonctionsentmake
/entmakex
pour écrire des données DXF directement dans la base de données de dessin.Remplir les attributs en référençant leurs noms de balises d'attribut afin de supprimer la dépendance sur l'ordre dans lequel les références d'attribut sont rencontrées dans la référence de bloc (qui pourrait être modifiée sur une base par dessin grâce à l'utilisation de la BATTMANcommande).
En utilisant une «valeur par défaut dynamique» (comme décrit dans mon tutoriel ), et potentiellement en stockant la valeur des valeurs par défaut entre les sessions de dessin.