AutoCAD... AutoLISP... VisualLISP...

  [15] Zmienne systemowe

index  

»  Zmienne systemowe

AutoCAD przechowuje nastawy (lub wartości) swojego środowiska operacyjnego i niektórych swoich poleceń w zmiennych systemowych. Każda zmienna systemowa ma przypisany typ: calkowity (integer), rzeczywisty (real), punkt (list) albo lańcuch tekstowy (string). Zmienne mogą być zapisywane w rysunku, w rejestrze Windows, lub niektóre w ogóle nie są zapamietywane. Jezeli zmienna systemowa nie jest zmienną tylko do odczytu (read only), to można sprawdzić i zmienić jej wartość bezpośrednio w linii poleceń, wykorzystując polecenie ZMSYS (_SETVAR) lub funkcje AutoLISP-u getvar (odczyt) i setvar (zapis), lub przez zmiany opcji niektórych poleceń i okien dialogowych.
Wywolanie funkcji getvar wygląda następująco:
(getvar [VAR (String)]) np.
(getvar "OSMODE") może zwrócić 183
lub
(getvar "LASTPOINT") zwraca (0.0 0.0 0.0)
Warto zauważyć że jeżeli zmienna nie wystepuje funkcja zwraca NIL:
(getvar "ABC") zwraca: Nil

Do zmiany wartości zmiennych (którym można ją zmienić) sluży funkcja setvar która wymaga dwóch argumentów:
  • nazwy zmiennej (STRING)
  • oraz poprawnej wartości tej zmiennej.
    Funkcja zwraca zmienioną wartość: (setvar "OSMODE" 0) zwraca: 0. Podanie niepoprawnych wartości argumentów (np. nazwy nieistniejącej zmiennej, złej wartości zmiennej itp.) powoduje błąd AutoLISP-a.

    »  "Hurtowa" zmiana wielu zmiennych

    Niestety używanie funkcji getvar jest nieco uciążliwe ponieważ dla każdej zmiany trzeba ją ponownie wywoływać. W razie potrzeby jednorazowej zmiany wielu zmiennych (zobacz: pułapki command), warto wykorzystać funkcję przedstawioną poniżej. Wymaga ona jednego argumentu w postaci listy na której umieszczone są naprzemiennie nazwy zmiennych i ich nowe wartości. Funkcja gdy nie wystąpił żaden błąd zwraca NIL. Dodatkowo wykorzystując nowe mozliwości Visual LISP-a, skonstruowałem funkcję tak aby była maksymalnie odporna na możliwe błędy (funkcja nic nie robi lub wykonuje tylko możliwe do zrealizowania działania):
  • jeżeli argument nie jest listą lub jest listą pustą
  • lista nie ma poprawnej dlugości - jezeli na liście znajdują się błędne nazwy zmiennych, złe typy wartości zmiennych, lub zmienna jest tylko do odczytu.
    W przypadku gdy wystąpi jakikolwiek błąd funkcja zwraca T.
    Przykłady:
    (jk:SYS_SetVars '("CMDECHO" 0)) i zmienia wartość zmiennej CMDECHO na 1
    (jk:SYS_SetVars '("CMDECHO" 1 "OSMODE" "ABC")) - zwraca: T i zmienia tylko wartość dla CMDECHO (OSMODE musi być INTeger)
    (jk:SYS_SetVars '("CMDECHO" 1 "OSMODE" 183 "WRITESTAT" 2)) - zmienia CMDECHO i OSMODE (zmienna WRITESTAT jest read-only), zwraca T. Funkcja wygląda tak:
  • ;;;
    ;;; Zmienia wartości zmiennych z listy podanej jako argument [l]
    ;;;
    (defun jk:SYS_SetVars (l / e)
      (if
        (listp l)
        (while l
          (if
            (setq e
              (vl-catch-all-error-p
                (vl-catch-all-apply
                  'setvar
                  (list (car l)(cadr l))
                )
              )
            )
            Nil
            (setvar (car l)(cadr l))
          )
          (setq l (cddr l))
        )
        (setq e T)
      )
      e
    )
    

    »  Zapamiętywanie wartości zmiennych systemowych

    O ile funkcja jk:SYS_SetVars może mieć wpływ na bardziej wydajną zmianę wielu zmiennych, do całości obrazu zarządzania zmiennymi systemowymi brakuje rozwiązania problemu przywracania poprzednich wartości zmiennych, które uległy zmianie. Najbardziej prostą i niewymagającą żadnych nakładow pracy (oprócz pisania kodu) jest stosowanie sekwencji:
      1). (setq old (getvar "VAR")) ; <- zapamietanie starej wartości
      2). (setvar "VAR" new) ; <- zmiana wartości
      3). ; *** - kod programu - ***
      4). (setvar "var" old) ; <- przywrócenie wartości zmiennej
    W przypadku zmiany wartości dla niewielu zmiennych, stosowanie takiej sekwencji jest proste i nie sprawia żadnego problemu, jednak wraz ze wzrostem ilości zmiennych do zmiany, łatwo zauważymy pewne niedogodności. Są to:
  • wzrost ilości zmiennych lokalnych do zapamietywania starych wartości
  • wobec powyzszego kontrola zmiennych lokalnych może stawać się uciążliwa
  • AutoLISP zajmuje więcej obszarów pamięci
  • narasta kod programu
    Wykorzystując cechy LISP-a najlepiej stosowac wszystko to co LISP robi najlepiej czyli operacje na listach. Od razu nasuwa sie proste rozwiązanie: przechowywać nazwy zmiennych i ich wartości na jednej liście (jako jeden symbol - zmienna globalna), i na podstawie tej listy przywracać te wartości z powrotem. Skojarzenie nazwy zmiennej systemowej i jej wartości, naturalnie narzuca konstrukcję tej listy - najlepszym rozwiązaniem wydaje sie byc lista par kropkowych, podobnie jak ma to miejsce przy listach DXF danych obiektow. Sposób ten jest powszechnie stosowany (wystarczy przejrzec standardowe pliki lsp AutoCAD-a i ExpressTools - funkcje modes i moder). Funkcja modes zapisuje liste z danymi zmiennych - a moder na podstawie tej listy przywraca te wartości.

    Na swoje potrzeby, napisalem podobnie działające dwie funkcje - ktore są bardziej elastyczne, posiadają ponadto dodatkowe cechy:
    • lista danych dla zmiennych jest zawsze zapisywana jako zmienna globalna o nazwie *jk-var
    • jeśli zmienna *jk-var ma wartość Nil, lista (zmienna) jest tworzona.
    • jeśli lista (zmienna *jk-var) istnieje nowe dane są dopisywane do listy (pod warunkiem ze nie ma juz ich na liście)
    • zapamietywane mogą być tylko nazwy i wartości isniejących zmiennych systemowych
    • przywracane wartości zmiennych mają tylko zastosowanie tylko dla właściwych zmiennych (nie read-only)
    • przywracanie zmiennych powoduje przypisanie zmiennej *jk-var wartośc Nil.
    Funkcja wygląda tak:
  • ;;; 
    ;;; Funkcja zapamietująca wartości zmiennych
    ;;;
    (defun jk:SYS_ModeS (l)
      (if
        (listp l)
        (if
          (setq l (vl-remove-if-not 'getvar l))
          (if
            (not *jk-Var)
            (setq *jk-Var (mapcar '(lambda (%)(cons % (getvar %))) l))
            (foreach % (mapcar '(lambda (%)(cons % (getvar %))) l)
              (if
                (not (car (assoc (car %) *jk-var)))
                (setq *jk-Var (append *jk-Var (list %)))
                Nil
              )
            )
          )
        )
      )
      *jk-Var
    )
    
    Argumentem funkcji jest lista, i jeśli znajdują sie na niej nazwy nieistniejących zmiennych systemowych zostają one odrzucone. Pozostałe elementy (poprawne nazwy zmiennych systemowych) łączone są w pary kropkowe (typu (nazwa_zmiennej . jej_wartość)) i zapisywane jako jedna lista. Lista jest zapisywana jako zmienna globalna *jk-Var (gdy nie istnieje), w przeciwnym wypadku do istniejącej listy dodawane są tylko nowe pary kropkowe. Pozwala to na wielokrotne wywołanie funkcji i aktualizowanie zmiennej *jk-Var. Funkcja zwraca utworzoną listę.
    Przykładowo:
    (jk:SYS_ModeS '("OSMODE" "BLIPMODE" "CMDECHO"))
    zwraca:
    (("OSMODE" . 183) ("BLIPMODE" . 0) ("CMDECHO" . 1))
    Następnie:
    (jk:SYS_ModeS '("CMDECHO" "ABC" "LASTPOINT"))
    zwraca:
    (("OSMODE" . 183)("BLIPMODE" . 0)("CMDECHO" . 1)("LASTPOINT" 0.0 0.0 0.0))
    LASTPOINT została dodana do listy, natomiast, nie zostały dodane: CMDECHO (bo już jest) i ABC bo nie jest zmienną systemową.

    »  Przywracanie wartości zmiennych systemowych

    Drugą funkcją zarządzająca zmiennymi systemowymi jest funkcja przywracająca wartości zmiennych na podstawie listy bedącą wartością zmiennej *jk-Var. Funkcja nie posiada zadnych argumentow, zawsze zwraca Nil, a wygląda tak:
    
    ;;; 
    ;;; Funkcja przywracająca wartości zmiennych
    ;;;
    (defun jk:SYS_ModeR ()
      (if
        *jk-Var
        (jk:SYS_SetVars
          (apply
            'append
            (mapcar '(lambda (%)(list (car %)(cdr %)))
                    *jk-var
            )
          )
        )
        Nil
      )
      (setq *jk-var nil)
    )
    
    Funkcja przekazuje funkcji jk:SYS_SetVars jako argument połączone w jedną listę pary kropkowe zmiennej *jk-var, której na koniec przypisuje wartość Nil.

    Teraz (uzbrojeni w takie narzędzia) zapewnienie prawidłowego, szybkiego i prostego sposobu zapisu, zmiany i przywracania zmiennych systemowych może wyglądać tak:
      1) (jk:SYS_ModeS '("OSMODE" "BLIPMODE" "CMDECHO"))
      2) (jk:SYS_SetVars '("OSMODE" 0 "BLIPMODE" 0 "CMDECHO" 0))
      3) *** kod programu ***
      4) (jk:SYS_ModeR)

    Warto pamietać że wywołanie (jk:SYS_ModeR) może występować w funkcjach obsługi błedów, jako wygodny sposob przywracania środowiska.

    Zobacz także: Wczytywanie zmiennych systemowych