Wir kennen Auswahlsätze aus der Zeichenarbeit mit AutoCAD - jede
Objektwahl (ausser dann, wenn es um eine Einzelwahl geht, z.B.
bei PEDIT) wird in einem Auswahlsatz zwischengespeichert. Auch in
AutoLisp können wir mit Auswahlsätzen arbeiten - es handelt sich
aber nicht um Lisp-Listen, sondern um einen eigenen Datentyp
(
(type ...) gibt das Symbol
PICKSET für einen
Auswahlsatz zurück).
Wir können also nicht mit den Listenfunktionen auf Auswahlsätze
zugreifen. Lisp stellt für das Arbeiten mit Auswahlsätzen die
folgenden Funktionen bereit:
- (ssget ...) zum Auswählen von Objekten
- (sslength ...) Länge eines Auswahlsatzes
- (ssname ...) Zugriff auf ein Entity
- (ssadd ...) Hinzufügen eines Entity
- (ssdel ...) Entfernen eines Entity
- (ssmemb ...) Testfunktion für Entity
Diese Liste ist nicht vollständig, es gibt noch drei weitere
Funktionen, auf die ich hier nicht eingehe, um die Sache halbwegs
übersichtlich zu halten.
(ssget ...) ist die komplexeste Funktion in AutoLisp, deshalb
gibt es zu diesem Thema ein eigenes Kapitel. Wir werden uns hier
darauf beschränken,
(ssget) ohne Argumente aufzurufen. In
diesem Fall hat der Benutzer die freie Wahl, wie und welche Objekte
er auswählt. Diese Objekte bekommen wir dann in Form eines
Auswahlsatzes zurück, den wir anschliessend mit den anderen oben
erwähnten Funktionen untersuchen und bearbeiten können.
Die Standard-Vorgehensweise beim Auswerten von Auswahlsätzen ist
die, dass in einer
(while)-Schleife der Satz durchgegangen
wird und über
(ssname) sukzessive auf die darin enthaltenen
Entitynamen zugegriffen wird. Dazu ein kleines Beispiel, das aber
einen kleinen Schönheitsfehler hat:
(defun info-test( / ss i entdata)
(setq ss(ssget))
(setq i 0)
(princ"Gewählte Objekte: \n")
(while(setq entdata(entget(ssname ss i)))
(princ(strcat(cdr(assoc 0 entdata))"\n"))
(setq i(1+ i))
)
)
Dieses Beispiel erfüllt seinen Zweck ja schon ganz gut, aber es
bricht am Ende mit einer Fehlermeldung ab! Die Fehlerstelle ist
schnell ermittelt:
(entget(ssname ss i)) verursacht den
Fehler, wenn wir über das Ende des Auswahlsatzes hinaus zugreifen
wollen -
(ssname) gibt dann
nil zurück, was der
Funktion
(entget) sauer aufstösst. Wir müssen also noch
eine weitere Variable dazwischensetzen:
(defun info-test( / ss i ent data)
(setq ss(ssget))
(setq i 0)
(while(setq ent(ssname ss i))
(setq data(entget ent))
(princ(strcat(cdr(assoc 0 data))"\n"))
(setq i(1+ i))
)
)
Dieses Beispiel hat gezeigt, wie man einen Auswahlsatz
auswertet. Kommen wir nun dazu, wie man einen Auswahlsatz
erzeugt - auch das nächste Beispiel ist noch nicht sehr
praxisorientiert, aber es demonstriert die Vorgehensweise:
(defun erzeugen-beispiel( / ss ents)
(setq ss(ssadd))
(while(setq ent(car(entsel)))
(ssadd ent ss)
)
ss
)
Der Auswahlsatz muss zuerst durch einen Aufruf von
(ssadd)
ohne Argumente erzeugt werden. Anschliessend kann der Benutzer
- jeweils einzeln - so viele Elemente wählen, wie er möchte.
Sie werden dem Auswahlsatz hinzugefügt. Wenn der Benutzer kein
weiteres Objekt mehr wählt, sondern einfach die Return-Taste
drückt, wird die Schleife beendet und der Auswahlsatz
zurückgegeben.
Auf eine Eigenheit der
(ssadd)-Funktion möchte ich
noch hinweisen: Es handelt sich um eine Seiteneffekt-Funktion!
Wir sind es ja z.B. von den Listenfunktionen gewohnt, den
Haupteffekt aufzufangen und einer Variablen zuzuweisen
bzw. ihn an die nächste Funktion weiterzuleiten. Bei
(ssadd) ist dies nicht notwendig - ausser beim ersten
Aufruf ohne Argumente natürlich. Da muss einer Variablen
zugewiesen werden, sonst erzeugen wir zwar einen Auswahlsatz,
können dann aber mangels eines Namens nicht darauf zugreifen.
Was ist, wenn der Benutzer ein Element mehrfach wählt? Dann
wird es dem Auswahlsatz doch doppelt eingefügt! - Nein, es
ist nicht so.
(ssadd) prüft ohne unser Zutun, ob ein
Element schon darin vorhanden ist und fügt es nur hinzu, wenn
dies nicht der Fall ist. Daher ist es nicht notwendig, vor
jedem Aufruf von
(ssadd) jedesmal mit
(ssmemb)
zu testen, ob ein Entity schon eingefügt wurde.
(ssmemb) bleibt also den Fällen vorbehalten, wo ein
Test unumgänglich wird, z.B. wenn mit zwei Auswahlsätzen
gearbeitet werden soll und sichergestellt sein soll, dass
ein Element nicht gleichzeitig in beiden Sätzen enthalten
sein darf.
(ssmemb) und
(ssdel), das ein
Entity aus einem Auswahlsatz entfernt, sind übrigens so
einfach in der Anwendung, dass ich hier auf weitere Beispiele
verzichten möchte.
Wesentlicher ist ein anderer Gedanke: Das Durcharbeiten
eines Auswahlsatzes in der oben beschrieben Form ist mühsam.
Listen bearbeiten wir mit
(foreach) oder
(mapcar),
wir brauchen dafür nicht jedesmal eine Schleifenkonstruktion
zu bauen und einen Zähler anzulegen. Wenn es um Auswahlsätze
geht, ist allerdings in AutoLisp kein Komfort zu finden -
da müssen wir selbst Hand anlegen.
Auf meinen Fortgeschrittenen-Seiten ist die Definition zu
einer Funktion
(ss-foreach ...)
zu finden. Ich stelle sie hier kurz vor:
(defun ss-foreach(ss expr / i)
(if ss
(repeat(setq i(sslength ss))
((eval expr)(ssname ss(setq i(1- i))))
)
)
)
Die Funktion erwartet einen Auswahlsatz als Argument sowie
einen
(lambda)-Ausdruck, der auf die im Auswahlsatz
enthaltenen Entities angewendet wird. Das umgebende
(if ...) ist übrigens notwendig, da Funktionen wie
(ssget) schlicht und einfach
nil zurückgeben,
wenn nichts gewählt wurde.
(sslength nil) gibt dann
also nicht 0 zurück (wie man es eigentlich erwarten könnte),
sondern verursacht einen Fehler.
Unser erstes Beispiel von oben noch einmal im neuen Gewand:
(defun info-test( / ss i ent data)
(ss-foreach(ssget)
'(lambda(ent / )
(princ
(strcat(cdr(assoc 0(entget ent))"\n")
)
)
)
)
Die Arbeit mit
(ssname) ist eigentlich unter dem
Niveau, das die Sprache Lisp hat. Wie man sieht, kann man
diese in Erdbodennähe stattfindenden Dinge sehr schön in
einer Bibliotheksfunktion kapseln und sich mit Dingen in
Kopfhöhe befassen. Ich habe in vielen Jahren des Programmierens
mit AutoLisp die Funktion
(ssname) jedenfalls nie
wieder benötigt (und die anderen sowieso nicht).
Übungsaufgaben
-
Kann man mit (ssadd) - ähnlich wie man mit (append)
zwei Listen zusammenfügt - auch zwei Auswahlsätze
zusammenfügen? Bitte ausprobieren!
-
Welche der folgenden Aussagen sind richtig?
-
Wenn man nur (ssadd ent ss) schreibt, verpufft die
Rückgabe, man muss also die Rückgabe mit setq wieder
zuweisen: (setq ss(ssadd ent ss))
-
Wenn man einem Auswahlsatz Entities hinzufügt, muss
man vorher mit (ssmemb) testen, ob das Entity
nicht bereits enthalten ist.
-
Ein Auswahlsatz kann maximal so lang sein, wie es
Elemente in der Zeichnung gibt.
-
(sslength) gibt bei einem leeren Auswahlsatz 0
zurück.
-
Bei (ssname) beginnt die Zählung genau wie bei (nth)
und (substr) bei 0, d.h. das erste Element eines
Auswahlsatzes erhalten wir mit (ssname 0 ss).
-
Schreiben Sie eine Funktion, die den Benutzer auffordert,
einige Entities zu wählen. Den Auswahlsatz legen Sie in
einer globalen Variablen namens *ss* ab. Probieren
Sie aus, ob Sie dann in AutoCAD innerhalb eines Befehls
mit !*ss* den Auswahlsatz in eine Objektwahl (z.B. bei
LÖSCHEN) 'einfüttern' können.
Lösungen