Autolisp nie ustawia poprawnie atrybutu bloku dla niektórych użytkowników
Pytasz za pierwszym razem, więc mam nadzieję, że wystarczająco dobrze opisuję problem.
W naszej firmie mamy kod Autolisp, który jest używany przez kilka osób z tą samą wersją AutoCAD, ale dla niektórych użytkowników lisp przestał działać poprawnie.
Funkcja seplenienia jest następująca:
- użytkownik uruchamia lisp
- program prosi o następujące rzeczy:
- skala bloku
- przedrostek tekstu w bloku
- numer porządkowy dla pierwszej jednostki bloku
- przyrost liczby bieżącej
- gdzie umieścić pierwszy blok
Powinno to prowadzić do bloku ze znacznikiem i tekstem o następującym formacie (prefiks) (możliwa część środkowa, jeśli numer nie składa się z trzech cyfr) (numer bieżący), np. PT001 lub PX100.
Zamiast tego jednak niektórzy użytkownicy doświadczyli braku prefiksu i numeru i zaczęli widzieć tylko wspomnianą wcześniej możliwą środkową część tekstu, podczas gdy innym razem ten sam użytkownik może doświadczyć, że wyświetlany jest tylko prefiks. Znacznik jest wyświetlany tak, jak powinien, ale tekst po prostu nie będzie działał zgodnie z oczekiwaniami.
Każda pomoc w przeanalizowaniu poniższego kodu pod kątem błędów jest bardzo mile widziana.
Jeśli kod wydaje się być „bezbłędny”, zakładam, że wystąpił problem z blokiem lub jego atrybutami.
-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)
)
Odpowiedzi
Istnieje wiele problemów z bieżącym kodem: niektóre z nich mogą być po prostu uznane za złą praktykę, niektóre spowodują awarię programu, jeśli użytkownik odpowie nieprawidłowymi danymi, a inne spowodują, że program zawiedzie lub zachowa się nieoczekiwanie, w zależności od ustawienia środowiska AutoCAD, w którym program jest wykonywany.
1. ATTREQ
Głównym winowajcą opisanego zachowania może być ATTREQzmienna systemowa, która określa, czy użytkownik otrzyma w ramach polecenia monit o podanie wartości atrybutów INSERT. Gdyby ATTREQ=0
program był uruchamiany, blok zostałby wstawiony z domyślnymi wartościami atrybutów.
Możesz zapewnić spójne zachowanie między środowiskami, przechowując bieżącą wartość tej zmiennej systemowej i ustawiając ją na 1
przed wywołaniem INSERTpolecenia (aby upewnić się, że monity o atrybuty są wyświetlane), a następnie przywracając oryginalną wartość po poleceniu lub na końcu polecenia program.
Na przykład:
(defun c:test ( / atr )
(setq atr (getvar 'attreq))
(setvar 'attreq 1)
;; ... Do your thing
(setvar 'attreq atr)
(princ)
)
2. OSMODE
Podczas dostarczania danych punktu do polecenia za pośrednictwem języka AutoLISP, na punkt będą miały wpływ wszystkie tryby lokalizacji obiektu aktywne w momencie podania punktu. Opisuję to bardziej szczegółowo w mojej odpowiedzi tutaj .
Najłatwiejszym sposobem uniknięcia tego jest użycie modyfikatora lokalizacji obiektu "_non"
lub w "_none"
celu poinstruowania programu AutoCAD, aby ignorował wszystkie tryby lokalizacji obiektów dla kolejnego wprowadzania punktów, np .:
(command "insert" "pointnumber" "_non" point sc sc 0 ph)
3. Wprowadzane przez użytkownika
Należy wziąć pod uwagę brak danych wejściowych użytkownika lub nieprawidłowe dane wejściowe użytkownika, aby uniknąć błędów podczas wykonywania programu - można to łatwo osiągnąć za pomocą if
instrukcji lub initget
funkcji, np .:
(initget 7) ;; Prevents Enter, zero, or negative numbers
(setq sc (getreal "\nEnter scale: "))
Lub:
(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
)
Alternatywnie możesz skonfigurować wartości domyślne dla każdego z tych monitów, używając jednej z metod, które opisuję w moim samouczku dotyczącym monitowania z opcją domyślną , na przykład:
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0)))
4. Zmienne lokalne a zmienne globalne
Obecnie wszystkie zmienne w twoim programie są zmiennymi globalnymi : to znaczy są zdefiniowane w przestrzeni nazw dokumentu (rysunku) i zachowają swoje wartości nawet po zakończeniu wykonywania programu.
Jak opisuję w moim samouczku dotyczącym zmiennych lokalizacyjnych , może to potencjalnie powodować problemy, jeśli takie zmienne przypadkowo współużytkują swoje nazwy ze zmiennymi globalnymi używanymi przez inne programy lub gdy program tworzy listę lub inną akumulacyjną strukturę danych w pętli.
O ile użycie zmiennej globalnej nie jest konieczne do poprawnego działania programu, sugerowałbym zadeklarowanie tych zmiennych jako lokalne dla funkcji, np .:
(defun c:pointnumber ( / ic inr md nr ph point px sc ) ;; Local variables
;; ...
)
5. Sprawdzanie istnienia bloku
Podanie nazwy bloku bezpośrednio do INSERTpolecenia zakłada, że albo definicja tego bloku już istnieje w aktywnym rysunku, albo rysunek o tej nazwie istnieje albo w katalogu roboczym, albo w ścieżce wyszukiwania plików pomocniczych programu AutoCAD - jeśli żaden z warunków nie jest spełniony, INSERTpolecenie Błąd podczas wykonywania programu.
Możesz zatem przetestować te warunki wcześniej, powiadamiając użytkownika, jeśli blok nie zostanie znaleziony, w przeciwnym razie przystąpisz do wykonania pozostałej części operacji:
(if
(or
(tblsearch "block" "pointnumber") ;; Checks for existing definition
(findfile "pointnumber.dwg") ;; Checks for drawing file
)
;; ...
)
Możesz także użyć tej cond
funkcji zamiast sekwencji if/else
wyrażeń.
6. Resetowanie środowiska w przypadku błędu
Ponieważ zmieniasz wartości zmiennych systemowych podczas wykonywania programu, powinieneś upewnić się, że środowisko AutoCAD użytkownika zostanie zresetowane do pierwotnego stanu w przypadku błędu podczas wykonywania programu - zauważ, że naciśnięcie przez użytkownika przycisku, Escaby zamknąć program, spowoduje również błąd.
Możesz to osiągnąć, definiując lokalną procedurę obsługi błędów, jak opisuję w moim samouczku dotyczącym obsługi błędów . Lokalna funkcja błędu jest oceniana w przypadku napotkania błędu podczas wykonywania programu, więc można uwzględnić wyrażenia w definicji tej funkcji, aby zresetować środowisko AutoCAD do jego pierwotnego stanu - w twoim przypadku oznaczałoby to zresetowanie pierwotnej wartości funkcji ATTDIA
zmienna systemowa.
Kładąc wszystko razem
;; 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
Istnieją inne możliwe ulepszenia, które można również zaimplementować, takie jak:
Usunięcie polegania na wywołaniach standardowych poleceń programu AutoCAD (w tym przypadku INSERTpolecenia) poprzez użycie
insertblock
metody ActiveX lub funkcjientmake
/entmakex
do zapisywania danych DXF bezpośrednio do bazy danych rysunku.Wypełnianie atrybutów przez odwoływanie się do ich nazw oznaczeń atrybutów, aby usunąć zależność od kolejności, w jakiej odniesienia do atrybutów występują w odniesieniu do bloku (co można modyfikować dla każdego rysunku za pomocą BATTMANpolecenia).
Używanie „dynamicznych ustawień domyślnych” (jak opisano w moim samouczku ) i potencjalnie przechowywanie wartości domyślnych między sesjami rysowania.