Ein paar Worte vorabHome   Letzte MeldungenNews   Index der Kapitel und der besprochenen FunktionenIndex   Wer ich bin, warum ich diese Seiten mache, KontaktImpressum   Ich freue mich über jeden Eintrag im Gästebuch!Gästebuch   Einige Links zu anderen AutoLisp-SeitenLinks   Copyrights und DisclaimerRechts
Hier können die kompletten Seiten als ZIP-File heruntergeladen werden!

Einführung zum Programmieren mit VisualLisp Welcome to...
Das ActiveX-Objektmodell - Grundlage der vl-Programmierung in AutoCAD Das Objekt
Die vla-Funktionen: Viel ActiveX - wenig Dokumentation Knielang
Funktionen für den schnellen Zugriff in VisualLisp Breiter Gürtel
Variants - der Gummi-Datentyp von VBA Damenhandtasche
Collections - VB-Sammelbehälter in VisualLisp Kommste mit rauf?
Das Auffangen von Fehlern in VisualLisp Plumps
Berechnen von Schnittpunkten zwischen Entities mit ActiveX Windschnittig!
Ein erster, einfacher Reaktor, der viel Arbeit sparen kann Faulheit
Importieren von Views aus einer geschlossenen Zeichnung mit DBX Deutsche Bahn


Zum Einsteiger-Tutorial

Zu den Seiten für Fortgeschrittene

Meine Private HP mit Fotos, Gedichten, Musik und Postkartenversand

Mein Online-Lexikon der Fotografie

Mein völlig abgedrehtes Reisebüro










Jedes Collection-Objekt, auf das wir im Zuge unserer ActiveX-Programmierung stossen, stellt uns die auf den ersten Blick sehr praktisch erscheinende Methode Item zur Verfügung - diese hat aber einen gravierenden Nachteil, der bereits in einem der vorigen Kapitel angesprochen wurde: Wenn wir auf ein nicht vorhandenes Element zugreifen wollen, wird ein Fehler verursacht. Noch einmal das Layer-Beispiel - wir greifen zunächst auf Layer "0" zu, da wir sicher sein können, dass dieser existiert:
(defun layer-test1( layer-name / )
  (vl-load-com)
  (vlax-invoke-method
    (vlax-get-property
      (vlax-get-property
        (vlax-get-acad-object)
        "ActiveDocument"
      )
      "Layers"
    )
    "Item"
    layer-name
  )
)

(layer-test1 "0")
  => #<VLA-OBJECT IAcadLayer 0fc04474>

(layer-test1 "NichtVorhanden")
  => ; Fehler: Automatisierungsfehler
               Schlüssel nicht gefunden
                  
Unser Zugrif auf den Layer "NichtVorhanden" würde so also mit einem Programm-Abbruch enden. Zwei verschiedene Strategien haben wir, um das zu verhindern:
  • Zuerst das Collection-Objekt durchsuchen, um festzustellen, ob ein bestimmtes Element darin vorhanden ist, und erst dann die Item-Methode anwenden

  • Auf gut Glück zugreifen und den Fehler gegebenenfalls mit geeigneten Mitteln abfangen
VisualLisp stellt uns zwei Funktionen zum Bearbeiten von Collection-Objekten zur Verfügung: (vlax-for ...) und (vlax-map-collection ...). Die Annahme liegt nahe, dass es hier um (foreach) und (mapcar) für Collections geht - das werden wir gleich untersuchen:
(defun test-vlax-for( / )
  (vlax-for item
    (vla-get-layers
      (vla-get-activedocument
        (vlax-get-acad-object)
      )
    )
    (print(vla-get-Name item))
  )
)

(test-vlax-for)
  => "0"
     "AndererLayer"
     "NochEinLayer""NochEinLayer"

!item
  => nil
                  
Dass die Layernamen von (print) ausgegeben werden - nun, etwas anderes war nicht zu erwarten. Und auch der anschliessende kleine Test mit !item zeigt, dass hier völlig analog zu (foreach) gearbeitet wird. Das Symbol item ist also lokal zu (vlax-for) und muss nicht als lokale Variable deklariert werden. Wir bemerken auch noch, dass der letzte Layer doppelt erscheint - das ist völlig OK, denn genauso arbeitet auch (foreach): Das eine Mal wurde der Layer von (print) ausgegeben, und daran schliesst sich die Funktionsrückgabe (Ergebnis der letzten Evaluation) an. Und nun ein Test, ob auch (vlax-map-collection) analog zu (mapcar) arbeitet:
(defun test-vlax-map-collection( / )
  (vlax-map-collection
    (vla-get-layers
      (vla-get-activedocument
        (vlax-get-acad-object)
      )
    )
    (function
      (lambda(item / )
        (vla-get-Name item)
      )
    )
  )
)

(test-vlax-map-collection)
  => #<VLA-OBJECT IAcadLayers 0fc04694>
                  
Das Ergebnis zeigt sofort, dass hier die Programmierer von AutoDesk das Klassenziel mal wieder völlig verfehlt haben: anstatt der erwarteten Liste mit den Layernamen wird das Collection-Objekt zurückgegeben. Fazit: Dem schönen heterogenen Paar (mapcar) (Effekt) / (foreach)(Seiteneffekt) wird hier das gleichartige Paar (vlax-map-collection) (Seiteneffekt) / (vlax-for) (auch Seiteneffekt) zur Seite gestellt.

Für die, die den im Einsteiger-Tutorial dargestellten Unterschied zwischen (foreach) und (mapcar) nicht so genau nachgelesen haben, den Hinweis, dies noch einmal schnell nachzuholen. Und wer sich über Effekt und Seiteneffekt nicht so ganz im Klaren ist: Das kann man in dem entsprechenden Kapitel 'Seitensprünge' im Fortgeschrittenen-Tutorial nachschlagen.

Noch einmal mit anderen Worten: (vlax-map-collection) ist zu nichts nütze - das, was diese Funktion macht, erledigt (vlax-for) genauso. Nehmen wir die Funktion (vlax-dump-object) wie im Hilfe-Beispiel:
; die eine Variante
(vlax-map-collection
  (vla-get-layers
    (vla-get-activedocument
      (vlax-get-acad-object)
    )
  )
 'vlax-dump-object
)

; und die andere Variante
(vlax-for item
  (vla-get-layers
    (vla-get-activedocument
      (vlax-get-acad-object)
    )
  )
  (vlax-dump-object item)
)
                  
Geringfügige Unterschiede in der Formulierung, aber sonst völlig gleiche Funktionsweise. Ein 'mehrdimensionales' Arbeiten wie bei (mapcar) ist sowieso nicht gegeben. Ach ja - (vlax-dump-object) ist eine ganz simple Funktion, die alles über ein Objekt auf dem Bildschirm ausgibt - etwa vergleichbar mit dem Liste-Befehl von AutoCAD. Auf (vlax-map-collection) können wir also wegen des Implementationsfehlers getrost verzichten.

Da uns also leider kein (mapcar) für die Collections zur Verfügung steht, müssen wir die Rückgabeliste von Hand bilden - was natürlich auch keine besonder schwere Aufgabe ist:
(defun collection-items(collection / r-liste)
  (vlax-for item collection
    (setq r-liste(cons item r-liste))
  )
  r-liste
)
                  
haben wir nun diese Funktion, können wir darauf aufbauend eine (member)-Funktion definieren, mit der wir gefahrlos testen können, ob ein Element Bestandteil der Collection ist. Da es sich im Gegensatz zu (member) um eine echte Prädikatfunktion handeln soll, kennzeichnen wir dies durch den Namenszusatz '-p'.
(defun collection-member-p(name collection / r-liste)
  (vlax-for item collection
    (setq r-liste
      (cons(vla-get-Name item)r-liste)
    )
  )
  (not(not(member name r-liste)))
)
                  
Hier gibt es eine Stelle, die vielleicht Irritation hervorrufen könnte - die doppelte Negation der Rückgabe. Eine kurze Erklärung dazu: (member) gibt ja bekanntlich den Listenrest ab dem gefundenen Element zurück. Dies soll aber verhindert werden - es soll, wie es sich für eine echte Prädikatfunktion gehört, entweder T oder nil zurückgegeben werden. Der Grund dafür ist, dass diese Funktion im nächsten Kapitel noch einmal defiert wird, und zwar mit Hilfe des Abfangen eines Fehlers. Dort kann natürlich gar kein Listenrest entstehen, es geht also nur darum, die beiden Versionen kompatibel zueinander zu halten.

Unser Beispiel mit Layer "NichtVorhanden" könnte jetzt also so formuliert werden, wie es anschliessend gezeigt wird. Den Code habe ich jetzt aber etwas überarbeitet, einerseits um ihn einfach zu verkürzen, andererseits um mal wieder zu zeigen, dass auch hier viele Wege nach Rom führen.
(defun layer-test2( layer-name / layers)
  (vl-load-com)
  (if
    (collection-member layer-name
      (setq layers
        (vla-get-Layers
          (vla-get-ActiveDocument
            (vlax-get-acad-object)
          )
        )
      )
    )
    (vla-item layers layer-name)
  )
)

(layer-test2 "0")
  => #<VLA-OBJECT IAcadLayer 0fc04474>

(layer-test2 "NichtVorhanden")
  => nil
                  
Das schlichte nil am Ende war das, worauf es ankommt - es wird das Auftreten eines Fehlers vermieden und nil zurückgegeben, wie man es eigentlich (Lisp-gemäss) erwarten würde.

Da das zweite Modell, wie man hier vorgehen könnte - den Fehler einfach abfangen - bringt einiges Neues ins Spiel. Deshalb werden wir uns im nächsten Kapitel ausschliesslich mit diesem Thema befassen.