Wer sich mit der ActiveX-Programmierung in AutoCAD befassen möchte, wird
zwangsläufig vor einen Baum rennen - man kommt einfach nicht dran vorbei.
Es ist der (natürlich nicht physikalische, sondern strukturelle) Baum der
ActiveX-Objekthierachie. Hier eine Abbildung, etwas verkleinert und zudem
noch ein bisschen schmaler gemacht, aber ansonsten genau so, wie man ihn
in der Online-Hilfe von VisualLisp findet (ActiveX and VBA Reference):
Beulen an der Stirn oder Schwellungen am Knie sind also infolge der
Virtualität dieses Gewächses nicht zu befürchten, leichte Kopfschmerzen
könnten aber beim Zusammenprall doch entstehen - wer das erste Mal damit
zu tun hat, muss mit den Folgen des 'impact' leben - ich benutze hier mal
das englische Wort, und das völlig im Einklang mit AutoDesk: Auch die
können sich offensichtlich keine Übersetzung leisten.
Dabei handelt es sich bei diesem Baum doch um ein völlig harmloses
Gewächs - man muss ihn nur einmal verstanden haben! Betrachten wir das
Ganze einmal farblich: Cyanfarben sind die geometrischen Datenbank-Objekte
(also das, was wir normalerweise 'Zeichnungs-Elemente' nennen), hellgrün die
nichtgrafischen Datenbank-Objekte (vorwiegend die Inhalte von Tabellen
wie z.B. die Layertabelle, aber auch Dictionaries, Gruppen, Layouts usw.),
und das Braun-Orange zeigt an, dass es hier um Dinge geht, die weder das
eine noch das andere sind: Hier geht's um die AutoCAD-Konfiguration, die
Menüs, das Plotten, Auswahlsätze usw.
Die einzelnen Elemente des Baums lassen sich aber auch nach der Form
unterscheiden, wobei hier nur zwei Fälle auftreten: Rechteckig sind die
'Collection Objects' - sagen wir mal, das sind Sammelbehälter für
Einzelobjekte. Die Einzelobjekte sind die mit den abgerundeten Seiten.
Ein einzelner Layer hat also runde Seiten, die Layertabelle als Ganzes
ist eckig.
Die Original-Abbildung in der AutoLisp-Hilfe hat allerdings einen
ganz entscheidenden Vorteil gegenüber meiner verkleinerten Abbildung:
Sie ist interaktiv - d.h. dass jeder Klick auf eines der abgebildeten
Elemente weiterführt. Allerdings landet man dann unweigerlich in der
Hilfe für Visual Basic - wer in Lisp programmieren möchte, ist darauf
angewiesen, sich so manches selbst 'zusammenreimen' zu müssen.
Wir lassen uns aber einfach nicht entmutigen und beleuchten die Dinge
noch ein wenig: Was sind denn diese Objekte, die das Objektmodell
ausmachen? Ich will hier gar nicht zu viel Theorie ins Spiel bringen -
unter Objekten versteht man im Sinne der objektorientierten Programmierung
erst einmal nichts weiter als zusammengesetzte Daten. Ein solches
Objekt repräsentiert etwas, z.B. einen Layer, und es hat zum Einen
Eigenschaften (z.B. einen Namen oder die Farbe des Layers) und zum
Anderen sog. Methoden - die Eigenschaften entsprechen immer einem
Variablenwert, der zum Objekt gehört, die Methoden sind ein bisschen
komplexer.
Wir kennen aus der AutoLisp-Programmierung Variablen und Funktionen,
neu ist hier nur der Ansatz, dass eine Variable (Eingenschaft) oder
eine Funktion (Methode) nicht mehr frei im Raum stehen und von jedem
Programmteil beliebig genutzt werden können, sondern dass sie an ein
Objekt gebunden sind und dann auch nur in diesem Zusammenhang nutzbar
sind. Als Drittes kommen dann noch die Ereignisse ins Spiel, die
ebenfalls zu den Objekten gehören - diese bleiben aber zunächst
'draussen' und werden erst später unter dem Stichwort 'Reaktoren'
behandelt.
Auf keinen Fall sollte man diese 'Objekte' mit den AutoCAD-Entities
verwechseln - die Objekte in diesem Baum gehen darüber hinaus. Die
Menüs sind z.B. genauso Objekte in diesem Baum wie die Voreinstellungen
oder auch die ganze Zeichnung (und alle anderen gleichzeitig geöffneten
Zeichnungen). Das 'alleroberste' Objekt ist übrigens die
AutoCAD-Applikation, wobei hier durchaus noch andere Objekte auf gleicher
Ebene geben kann, wenn andere Applikationen wie Excel mit ins Spiel
kommen.
Wir werden zunächst einmal klären, wie man nun überhaupt auf diese
Objekte zugreifen kann und zunächst einmal versuchen, ihnen ein paar
Werte zu entlocken. Wir bleiben einfach mal bei den Layern: Aus der
Grafik können wir sehen, dass der Weg bei 'Application' beginnt (da
beginnt er immer, weil das - wie gesagt - das oberste Objekt ist).
Von da aus müssen wir weiter nach 'Documents', dann zu einem bestimmten
'Document', und von da aus dann zu 'Layers'. Dieser Weg ist
nachvollziehbar: In der AutoCAD-Sitzung (Application) sind mehrere
Zeichnungen (Documents) geöffnet, und in einer davon, nämlich der, die
gerade bearbeitet wird (Document), finden wir die Layertabelle (Layers),
um die es uns geht.
Um an das Ober-Objekt 'Application' zu kommen, benutzen wir immer die
Funktion
(vlax-get-acad-object) - an dieser Funktion führt kein
weg vorbei. Und ebenso kommen wir um einen Aufruf von
(vl-load-com)
vorbei - dieser Aufruf lädt erst die ganze ActiveX-Unterstützung und
ist daher in jedem ActiveX-Programm zwingend notwendig. Machen wir uns
eine kleine Testfunktion und schauen mal, was dabei herauskommt:
(defun test1( / )
(vl-load-com)
(vlax-get-acad-object)
)
(test1) = > #<VLA-OBJECT IAcadApplication 00b07a68>
Die Rückgabe beruhigt, weil 'IAcadApplication' sehr danach aussieht,
als hätte alles geklappt, und gleichzeitig beunruhigt sie etwas, da wir
es hier mit dem völlig neuen Datentyp 'VLA_OBJECT' zu tun haben.
Wenn wir in der AutoCAD-Hilfe auf 'Application' klicken, gelangen wir
auf die Seite, auf der die Methoden und Properties(= Eigenschaften)
von 'Application' aufgelistet sind: Unser Interesse wird von der
Eigenschaft 'ActiveDocument' geweckt. Tatsächlich können wir uns damit
den Umweg über 'Documents' sparen und direkt zur aktiven Zeichnung
springen. Um eine Eigenschaft eines Objekts auszulesen, benötigen
wir
(vlax-get-property). Erstes Argument ist das Objekt, das
zweite Argument ist der Name der Eigenschaft:
(defun test2( / )
(vl-load-com)
(vlax-get-property
(vlax-get-acad-object)
"ActiveDocument"
)
)
(test2) = > #<VLA-OBJECT IAcadDocument 00bd0bcc>
Auch das sieht gut aus und lässt hoffen - ein weiterer Klick auf die
Original-Grafik in der Online-Hilfe zeigt uns, dass das gesuchte
Layer-Objekt eine Eigenschaft von 'Document' ist. Folgerichtig
müssen wir also einen weiteren Aufruf von
(vlax-get-property)
in unsere Funktion einbauen:
(defun test3( / )
(vl-load-com)
(vlax-get-property
(vlax-get-property
(vlax-get-acad-object)
"ActiveDocument"
)
"Layers"
)
)
(test3) = > #<VLA-OBJECT IAcadLayers 00c254d4>
Nun zeigt uns allerdings ein neuer Klick auf 'Layers', dass hier
erst einmal das Ende der Fahnenstange erreicht ist: 'Layers' hat
keine Eigenschaften, die wir jetzt nutzen könnten, um irgendwie
einen Layernamen oder ähnliches herauszufinden. Das liegt daran,
dass 'Layers' ein Collection-Objekt ist - das waren doch die Objekte,
die in der Grafik abgerundet sind. Auch 'Documents' ist ein solches
Collection-Objekt, aber das haben wir ja ganz geschickt übersprungen.
Collection-Objekte sind immer nichts weiter als Sammelbehälter
für andere Objekte. Um einen Blick in den Topf zu werfen, stellt
VisualLisp verschiedene Möglichkeiten zur Verfügung. Z.B. verfügt
jedes Collection-Objekt über die Methode 'Item', mit der man auf
ein darin enthaltenes einzelnes Objekt zugreifen kann - vorausgesetzt,
man kennt den Namen. Bei den Layern wissen wir, dass "0" immer ein
gültiger Name ist - dieser Layer muss in jeder Zeichnung vorhanden
sein.
Während
(vlax-get-property) für den Zugriff auf Eigenschaften
zuständig ist, dient die Funktion
(vlax-invoke-method) dazu,
Objektmethoden aufzurufen und auszuführen. Die Syntax ist hier etwas
variabler: Als erstes Argument wird wieder das Objekt übergeben, dann
folgt der Name der Methode als Zeichenkette, und daran kann sich dann
eine variable Anzahl von weiteren Argumenten anfügen, die davon
abhängt, wie viele Argumente die auszuführende Methode erwartet.
In unserem Fall geht es nur um ein drittes Argument, nämlich den
Layernamen:
(defun test4( / )
(vl-load-com)
(vlax-invoke-method
(vlax-get-property
(vlax-get-property
(vlax-get-acad-object)
"ActiveDocument"
)
"Layers"
)
"Item"
"0"
)
)
(test4) = > #<VLA-OBJECT IAcadLayer 00c26064>
Nun, wir haben tatsächlich einen Layer zurückerhalten! Versuchen
wir doch einmal, die Eigenschaft 'Freeze' auszulesen (diese finden
wir natürlich wieder durch einen Klick auf 'Layer' in der grossen
bunten Grafik in der Hilfe):
(defun test5( / )
(vl-load-com)
(vlax-get-property
(vlax-invoke-method
(vlax-get-property
(vlax-get-property
(vlax-get-acad-object)
"ActiveDocument"
)
"Layers"
)
"Item"
"0"
)
"Freeze"
)
)
(test5) = > :vlax-false
Sieh da, zur Abwechslung kein Objekt, sondern ein Symbol! An dem
Doppelpunkt stören wir uns nicht, er ist ohne Relevanz und nur
aus einer Laune der Autodesk-Programmierer in den Symbolnamen
hineingeraten. Nun, das bedeutet einfach: Der Layer ist nicht
gefroren. Sollte er bei Ihnen gefroren gewesen sein, dann muss
das Ergebnis natürlich
:vlax-true lauten.
Schöner und einfacher wäre es, wenn in solchen Situationen wie
gewohnt
T oder
nil zurückkäme - aber auf diesen
Luxus müssen wir verzichten! All die ActiveX-Funktionen werden
doch überhaupt nicht vom AutoLisp-Interpreter evaluiert, sondern
von in C/C++ geschriebenem Code innerhalb von AutoCAD. Lisp ist
hier nur noch Schnittstelle. Wir haben uns also mit den im
AutoCAD-Code herrschenden Datentypen abzufinden - da gibt es
den Datentyp 'boolean' mit den Werten 'true' und 'false'. Die
beiden Symbole ':vlax-true' und ':vlax-false' spiegeln das wider.
Zum Abschluss des Kapitels gönnen wir uns noch ein Erfolgserlebnis
und lesen die Farbe des Layers "0" aus:
(defun test6( / )
(vl-load-com)
(vlax-get-property
(vlax-invoke-method
(vlax-get-property
(vlax-get-property
(vlax-get-acad-object)
"ActiveDocument"
)
"Layers"
)
"Item"
"0"
)
"Color"
)
)
(test6) = > 7
7 (weiss) war der Layer "0" jedenfalls bei mir. Na also, klappt doch!
Im nächsten Kapitel werden wir dann versuchen, den Code ein bisschen
zu kürzen!