Visual LISP and Layers
I have had so many queries lately regarding Visual Lisp and Layers, that I decided to dedicated a complete section dedicated to this subject. Please remember though, that what you read here is not all encompassing in regards to Layers and could possible be added to over time. (in other words, I'm making excuses in case I miss anything).
Layers in AutoCAD are contained within the Layers Collection which is stored in the Document Object which is part of the Documents Collection which is part of the Application Object or AutoCAD itself. Confused? Have a look at an extract from the AutoCAD Object Model :
Application(Object) | | |------- Documents[Collection] | | | | | | | Document(Object) | | | | | |------ Layers-------Layer | | [Collection] (Object) | | | | | | | |
To retrieve a Layer to play
with, we first need to access the Layers Collection.
Type this at the Console prompt :
_$ (vl-load-com)
_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>
We are now in the Application Object. Now let's sneak into the Document Object :
_$ (setq activedocument
(vla-get-activedocument acadobject))
#<VLA-OBJECT IAcadDocument 01945554>
And into the Layers Collection we go!
_$ (setq LayerTable (vla-get-layers
activedocument))
#<VLA-OBJECT IAcadLayers 01a070bc>
"But, hang on one
second, you jumped from the Application Object straight to the Document
Object!!"
Hey, well spotted. I'm glad to see that you're wide awake. OK, I suppose
an explanation would be in order.
Let's run a dump on the Application Object :
_$ (vlax-dump-object
acadobject)
; IAcadApplication: An instance of the AutoCAD
application
; Property values:
; ActiveDocument = #<VLA-OBJECT
IAcadDocument 01945504>
; Application (RO) = #<VLA-OBJECT IAcadApplication 00adc088>
; Caption (RO) = "AutoCAD 2000 - [Drawing1.dwg]"
; Documents (RO) = #<VLA-OBJECT IAcadDocuments 02d06860>
; FullName (RO) = "C:\\ACAD2000\\acad.exe"
; Height = 776
; LocaleId (RO) = 1033
; MenuBar (RO) = #<VLA-OBJECT IAcadMenuBar 02d096c4>
; MenuGroups (RO) = #<VLA-OBJECT IAcadMenuGroups 015baf2c>
; Name (RO) = "AutoCAD"
; Path (RO) = "C:\\ACAD2000"
; Preferences (RO) = #<VLA-OBJECT IAcadPreferences 015bb9ec>
; StatusId (RO) = ...Indexed contents not shown...
; VBE (RO) = #<VLA-OBJECT VBE 03404a04>
; Version (RO) = "15.0h (Hardware Lock)"
; Visible = -1
; Width = 1032
; WindowLeft = -4
; WindowState = 3
; WindowTop = -4
T
Do you see what I see? The Active Document Object is a Property of the Application Object. This means that we can access it directly without having to retrieve it from the Documents Collection. Clever hey?
Still not convinced. OK, let's do it the long way around :
_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>
Again we've accessed the
Application Object. Now let's get into the Documents Collection :
_$ (setq documentcollection (vla-get-documents acadobject))
#<VLA-OBJECT IAcadDocuments 01a03d30>
Now, we'll retrieve the Document Object for the drawing
_$ (setq thedocument (vla-item
documentcollection 0))
#<VLA-OBJECT IAcadDocument 01a6f6a4>
And finally we retrieve the
Layers Collection.
_$ (setq LayerTable (vla-get-layers thedocument))
#<VLA-OBJECT IAcadLayers 02fdca64>
But, what happens if we
have two or more drawings open and we want to access the Layers Collection
of one of the other drawings?
Let's think about this. Where would the inactive documents (your other
drawings) be stored?
I would say in the Documents Collection wouldn't you agree?
Let's try this out. First of all open two new drawings, Drawing1.dwg and
Drawing2.dwg. Ensure that Drawing1.dwg is the active drawing. Now type
this at the Console prompt.
_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>
Again we've accessed the
Application Object. Now let's get into the Documents Collection :
_$ (setq documentcollection (vla-get-documents acadobject))
#<VLA-OBJECT IAcadDocuments 01a03d30>
Now, we'll retrieve the Document Object for the specific inactive drawing
_$ (setq thedocument (vla-item
documentcollection "Drawing2.dwg"))
#<VLA-OBJECT IAcadDocument 01a6f6a4>
And finally we retrieve the
Layers Collection.
_$ (setq LayerTable (vla-get-layers thedocument))
#<VLA-OBJECT IAcadLayers 02fdca64>
Let's test this out. Let's change the current Layer in Drawing2.dwg to Layer 0. Ensure that Drawing1.dwg is still the active drawing and enter this at the Console prompt :
_$ (vla-put-activelayer
thedocument (vla-item LayerTable 0))
nil
Switch to Drawing2.dwg. Layer 0 should have become the current Layer.
To avoid confusing you any further by dealing with multiple workspaces, let's go back to just one active drawing. Close all drawings and open a new drawing. Enter this at the Console prompt :
_$ (setq acadobject (vlax-get-Acad-Object))
#<VLA-OBJECT IAcadApplication 00adc088>
_$ (setq activedocument (vla-get-activedocument acadobject))
#<VLA-OBJECT IAcadDocument 01945554>
_$ (setq LayerTable (vla-get-layers activedocument))
#<VLA-OBJECT IAcadLayers 01a070bc>
No messing
about this time! Straight to the Layers Collection.
Right, down to the nitty gritty. First create a Layer in your drawing
named "TestLayer".
OK, now let's access "TestLayer" from the Layers Collection :
_$ (setq theLayer
(vla-item LayerTable "TestLayer"))
#<VLA-OBJECT IAcadLayer 02fce56c>
Let's list the properties
and methods of this Layer :
_$ (vlax-dump-object theLayer T)
; IAcadLayer: A logical grouping of data, similar to
transparent acetate overlays on a drawing
; Property values:
; Application (RO) = #<VLA-OBJECT IAcadApplication 00adc088>
; Color = 7
; Document (RO) = #<VLA-OBJECT IAcadDocument 01945554>
; Freeze = 0
; Handle (RO) = "957"
; HasExtensionDictionary (RO) = 0
; LayerOn = -1
; Linetype = "CONTINUOUS"
; Lineweight = -3
; Lock = 0
; Name = "TestLayer"
; ObjectID (RO) = 26864120
; ObjectName (RO) = "AcDbLayerTableRecord"
; OwnerID (RO) = 26862608
; PlotStyleName = "Color_12"
; Plottable = -1
; ViewportDefault = 0
; Methods supported:
; Delete ()
; GetExtensionDictionary ()
; GetXData (3)
; SetXData (2)
T
To create a new Layer, we simply add it to the Layers Collection :
_$ (setq aNewLayer
(vla-add LayerTable "NewLayer"))
#<VLA-OBJECT IAcadLayer 02fce7bc>
You should have a new Layer
in your drawing named "NewLayer".
But, our new Layer has been created with default colour and linetype
values namely, 7 and Continuous.
Let's change them :
_$ (vla-put-color aNewLayer 2)
nil
Changes the colour of our Layer to 2 (yellow).
_$ (vla-put-linetype aNewLayer "Dashed2")
nil
Changes the linetype of our
Layer to "Dashed2". "Dashed 2" of course, must be
loaded within our drawing.
Conversely, if you want to find the Colour and Linetype of a particular
Layer you would do this :
_$ (vla-get-color
aNewLayer)
2
_$ (vla-get-linetype aNewLayer)
"DASHED2"
Let's play around with our
new Layer. First, change to any other Layer in your drawing.
OK, let's switch our Layer OFF and then back ON :
_$ (vla-put-layeron
aNewLayer :vlax-false)
nil
_$ (vla-put-layeron aNewLayer :vlax-true)
nil
Next we'll FREEZE our Layer :
_$ (vla-put-freeze
aNewLayer :vlax-true)
nil
And to THAW the Layer :
(vla-put-freeze
aNewLayer :vlax-false)
nil
Right, now we'll LOCK it :
_$ (vla-put-Lock
aNewLayer :vlax-true)
nil
And now UNLOCK the Layer :
_$ (vla-put-Lock
aNewLayer :vlax-false)
nil
Should we now make the Layer UNPLOTTABLE?
_$ (vla-put-plottable
aNewLayer :vlax-false)
nil
Now we'll make the Layer PLOTTABLE :
_$ (vla-put-plottable
aNewLayer :vlax-true)
nil
Want to change the Layer's LINEWEIGHT? Let's change it to 0,35mm :
_$ (vla-put-LineWeight
aNewLayer 35)
nil
Let's change the Lineweight back to DEFAULT :
_$ (vla-put-LineWeight
aNewLayer -3)
nil
"ByLwDefault" =
-3
"ByBlock" = -2
"ByLayer" = -1
Other Values are : 0, 5, 9, 13, 15, 18, 20, 25, 30, 35, 40, 50, 53, 60,
70, 80, 90, 100, 106, 120, 140, 158, 200, 211.
In AutoCAD, make our new
Layer the current Layer, and draw a line.
Now let's delete our new Layer :
_$ (vla-delete
aNewLayer)
; error: Automation Error. Object is referenced by
other object(s)
Oh, oh. We have an error.
Think about it! How can we delete the Layer if it is being referenced by
our Line Object?
OK, Let's delete the Line and try again :
_$ (vla-delete aNewLayer)
; error: Automation Error. Object is referenced by
other object(s)
What, still an error?
That's because the Layer is current and is therefore referenced by the
Document Object.
Now make any other Layer current and try for a third time :
_$ (vla-delete aNewLayer)
nil
Hurray, success at last. The Layer is now an "Ex-Layer".
Here's a couple of Layer routines written using Visual Lisp that you may find useful.
Turn All Layers ON :
(defun C:Layeron ( / acadDocument theLayers) (vl-load-com) (setq acadDocument (vla-get-activedocument (vlax-get-acad-object))) (setq theLayers (vla-get-layers acadDocument)) (vlax-for item theLayers (vlax-put-property item "LayerOn" ':vlax-true) ) (princ) );defun (princ)
This routine will place a Prefix in front of all Layer names
;and rename them.Of course, it will not rename Layer "0" or "Defpoints".
(prompt "\nType ChLayName to run.........")
(defun C:ChLayName ( / acadDocument theLayers layName pre)
(vl-load-com)
(setq pre (getstring "\nEnter Layer Prefix : "))
(setq acadDocument (vla-get-activedocument (vlax-get-acad-object)))
(setq theLayers (vla-get-layers acadDocument))
(vlax-map-collection theLayers 'layer-mod)
(princ)
);defun
(defun layer-mod (theLayer)
(setq layName (vlax-get-property theLayer 'Name))
(if (not (member layName '("0" "Defpoints")))
(vla-put-Name thelayer (strcat pre layName))
) ;if
);defun
(princ)
Create a layer using Visual Lisp?
;;;Returns a layer object or nil
;;;on creation failure
(defun mLayer (LayerName)
(vl-load-com)
(setq LayerName
(vl-catch-all-apply
'vla-add
(list
(vla-get-layers
(vla-get-activedocument
(vlax-get-acad-object)
)
)
Layername
)
)
)
(if (vl-catch-all-error-p LayerName)
nil
LayerName
)
)
This routine will return a list of all Layers in the active drawing :
(defun C:LayList ( / acadobject activedocument LayerTable thelist)
(vl-load-com)
(setq acadobject (vlax-get-Acad-Object))
(setq activedocument (vla-get-activedocument acadobject))
(setq LayerTable (vla-get-layers activedocument))
(vlax-for each LayerTable
(setq thelist (cons (vla-get-Name each) thelist))
)
(if thelist (reverse thelist))
);defun
(princ)