Bill Tillman Posted January 9, 2012 Posted January 9, 2012 I followed some suggestions from the AfraLISP website on how to turn layers on and off but seem to be stumped with getting the syntax correct. The layers in the file I'm working with are named in all kinds of formats...some with dashes and spaces, some without. That part I figured out but I can't seem to get this part of my code to work: . . . (setq acadobject (vlax-get-Acad-Object)) (setq activedocument (vla-get-activedocument acadobject)) (setq LayerTable (vla-get-layers activedocument)) ; START TURNING OFF LAYERS ----------------------------------------------------- (if (= OpenHorzSpring "N") (vla-put-layeron "A-Open springs" :vlax-false) ; F12 ) (if (= OpenVertSpring "N") (vla-put-layeron "A-Open springs" :vlax-false) ; F14 ) . . . I'm getting this error message when I run the program: ; error: bad argument type: VLA-OBJECT "A-Open springs" A-Open springs is the name of the layer I want to turn off. The layer does exist and it's spelled exactly like I have it in the code. I've tried it with and without quotes. The AfraLISP directions showed a small "a" in front of the layername and I have tried it that was as well. If anyone can offer some pointers here I would appreciate it. Quote
rkmcswain Posted January 9, 2012 Posted January 9, 2012 vla-put-layeron is expecting an Object, not a String. Try something like this. (vla-put-layeron (vlax-invoke-method LayerTable "Item" "A-Open springs") :vlax-false ) Quote
Bill Tillman Posted January 10, 2012 Author Posted January 10, 2012 Thanks. I had some troubles with that but eventually got all the parens straight and it's working fine. Nothing was wrong with your code, it must have been my fat fingers which kept me in the dark again. Is this the best way to handle the task or is there a more efficient method? Quote
Tharwat Posted January 10, 2012 Posted January 10, 2012 (vla-put-layeron (vla-item (vla-get-layers (vla-get-activedocument (vlax-get-acad-object ))) "A-Open springs") :vlax-false) Quote
irneb Posted January 10, 2012 Posted January 10, 2012 That's the most "efficient" method I can think of. You could of course just use command calls to the command-line Layer command. Or you could obtain the DXF data of the layer through tblobjname & entget, then using subst & assoc & entmod you could change the layer's colour to negative. But IMO both those are either too complex to write and/or to slow in comparison to the ActiveX way. I'd go the entmod way only if ActiveX is unavailable (e.g. in ACad for Mac / BricsCAD for Linux). Be warned though changing the colour using entmod may change the layer's colour if RGB/HSV/Colour Book colour is used. Quote
rkmcswain Posted January 10, 2012 Posted January 10, 2012 Is this the best way to handle the task or is there a more efficient method? Mine was just a quick, walking out the door, dump of code in my head... As others have pointed out since, there are other methods available. Quote
Lee Mac Posted January 10, 2012 Posted January 10, 2012 Firstly, to clarify, you are receiving an error because the LayerOn property is derived from a VLA Layer Object and hence requires this object as the first parameter. Each of the following will produce the same result: ([color=blue]vla-put-layeron[/color] [color=darkgreen]<VLA Layer Object>[/color] [color=red]<:vlax-true / :vlax-false>[/color]) ([color=blue]vla-put-property[/color] [color=darkgreen]<VLA Layer Object>[/color] [color=darkred]"LayerOn"[/color] [color=red]<:vlax-true / :vlax-false>[/color]) ([color=blue]vlax-put[/color] [color=darkgreen]<VLA Layer Object>[/color] [color=darkred]"LayerOn"[/color] [color=red]<actrue / acfalse>[/color]) A few notes: The property argument "LayerOn" can be a string or quoted symbol 'LayerOn The undocumented vlax-put function operates with native AutoLISP data types and hence uses the actrue / acfalse symbols in place of :vlax-true / :vlax-false This object can be retrieved from the Layers Collection using the Item method of that collection. ([color=blue]vla-item[/color] [color=green]<VLA Layers Collection>[/color] [color=red]<Layer Name>[/color]) ([color=blue]vlax-invoke-method[/color] [color=green]<VLA Layers Collection>[/color] [color=darkred]"Item"[/color] [color=red]<Layer Name>[/color]) ([color=blue]vlax-invoke[/color] [color=green]<VLA Layers Collection>[/color] [color=darkred]"Item"[/color] [color=red]<Layer Name>[/color]) However, be aware that this method will throw an exception if the Layer does not exist in the Layers Collection: ([color=BLUE]setq[/color] acdoc ([color=BLUE]vla-get-activedocument[/color] ([color=BLUE]vlax-get-acad-object[/color])) aclay ([color=BLUE]vla-get-layers[/color] acdoc) ) ([color=BLUE]vla-item[/color] aclay [color=MAROON]"This Layer does not Exist"[/color]) [color=darkred]; error: Automation Error. Key not found[/color] So some additional error trapping may be required to catch this exception should the Item method be passed a Layer name that does not exist in the drawing. Consider the following Visual LISP function to turn a Layer ON & OFF: [color=GREEN];; layer - name of layer[/color] [color=GREEN];; flag - t=ON, nil=OFF[/color] ([color=BLUE]defun[/color] vl-LayerOnOff ( layer flag [color=BLUE]/[/color] obj ) ([color=BLUE]if[/color] ([color=BLUE]null[/color] ([color=BLUE]vl-catch-all-error-p[/color] ([color=BLUE]setq[/color] obj ([color=BLUE]vl-catch-all-apply[/color] '[color=BLUE]vla-item[/color] ([color=BLUE]list[/color] ([color=BLUE]vla-get-layers[/color] ([color=BLUE]vla-get-activedocument[/color] ([color=BLUE]vlax-get-acad-object[/color]))) layer) ) ) ) ) ([color=BLUE]not[/color] ([color=BLUE]vla-put-layeron[/color] obj ([color=BLUE]if[/color] flag [color=BLUE]:vlax-true[/color] [color=BLUE]:vlax-false[/color]))) ) ) This function could be called in the following way to turn Layer "0" off: (vl-LayerOnOff [color=MAROON]"0"[/color] [color=BLUE]nil[/color]) Should the layer name not exist in the drawing, this function will return nil without error. Since the process of retrieving the Layers Collection from the Active Document Object, and retrieving the Active Document from the Application Object is inefficient, I would be inclined to rewrite the above function to accept the Layers Collection as another argument: [color=GREEN];; layers - Layers Collection[/color] [color=GREEN];; layer - name of layer[/color] [color=GREEN];; flag - t=ON, nil=OFF[/color] ([color=BLUE]defun[/color] vl-LayerOnOff ( layers layer flag [color=BLUE]/[/color] obj ) ([color=BLUE]if[/color] ([color=BLUE]null[/color] ([color=BLUE]vl-catch-all-error-p[/color] ([color=BLUE]setq[/color] obj ([color=BLUE]vl-catch-all-apply[/color] '[color=BLUE]vla-item[/color] ([color=BLUE]list[/color] layers layer)) ) ) ) ([color=BLUE]not[/color] ([color=BLUE]vla-put-layeron[/color] obj ([color=BLUE]if[/color] flag [color=BLUE]:vlax-true[/color] [color=BLUE]:vlax-false[/color]))) ) ) This way, the function can be called multiple times without having the retrieve the Layers Collection each time. An alternative would be to use a Vanilla AutoLISP method to turn the layer on and off. Vanilla AutoLISP is generally faster and requires less error trapping since many of its functions return nil instead of throwing an exception when an invalid parameter is passed. Here is the same function written using Vanilla AutoLISP: [color=GREEN];; layer - name of layer[/color] [color=GREEN];; flag - t=ON, nil=OFF[/color] ([color=BLUE]defun[/color] LayerOnOff ( layer flag [color=BLUE]/[/color] co en ) ([color=BLUE]if[/color] ([color=BLUE]setq[/color] en ([color=BLUE]tblobjname[/color] [color=MAROON]"LAYER"[/color] layer)) ([color=BLUE]progn[/color] ([color=BLUE]setq[/color] en ([color=BLUE]entget[/color] en) co ([color=BLUE]abs[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 62 en))) ) ([color=BLUE]entmod[/color] ([color=BLUE]subst[/color] ([color=BLUE]cons[/color] 62 ([color=BLUE]if[/color] flag co ([color=BLUE]-[/color] co))) ([color=BLUE]assoc[/color] 62 en) en)) ) ) ) A reference for the Layer DXF Group codes can be found here. Quote
alanjt Posted January 10, 2012 Posted January 10, 2012 One more method... ;; layer - name of layer ;; flag - t=ON, nil=OFF (defun vl-LayerOnOff2 (layer flag / obj) (if (setq obj (tblobjname "LAYER" layer)) (vla-put-layeron (vlax-ename->vla-object obj) (if flag :vlax-true :vlax-false ) ) ) ) Quote
irneb Posted January 10, 2012 Posted January 10, 2012 Since the process of retrieving the Layers Collection from the Active Document Object, and retrieving the Active Document from the Application Object is inefficient, I would be inclined to rewrite the above function to accept the Layers Collection as another argument: [color=GREEN];; layers - Layers Collection[/color] [color=GREEN];; layer - name of layer[/color] [color=GREEN];; flag - t=ON, nil=OFF[/color] ([color=BLUE]defun[/color] vl-LayerOnOff ( layers layer flag [color=BLUE]/[/color] obj ) ([color=BLUE]if[/color] ([color=BLUE]null[/color] ([color=BLUE]vl-catch-all-error-p[/color] ([color=BLUE]setq[/color] obj ([color=BLUE]vl-catch-all-apply[/color] '[color=BLUE]vla-item[/color] ([color=BLUE]list[/color] layers layer)) ) ) ) ([color=BLUE]not[/color] ([color=BLUE]vla-put-layeron[/color] obj ([color=BLUE]if[/color] flag [color=BLUE]:vlax-true[/color] [color=BLUE]:vlax-false[/color]))) ) ) This way, the function can be called multiple times without having the retrieve the Layers Collection each time. Another benefit of this is you could then use it on ObjectDBX documents - i.e. editing DWG files without opening them. Here is the same function written using Vanilla AutoLISP: [color=GREEN];; layer - name of layer[/color] [color=GREEN];; flag - t=ON, nil=OFF[/color] ([color=BLUE]defun[/color] LayerOnOff ( layer flag [color=BLUE]/[/color] co en ) ([color=BLUE]if[/color] ([color=BLUE]setq[/color] en ([color=BLUE]tblobjname[/color] [color=MAROON]"LAYER"[/color] layer)) ([color=BLUE]progn[/color] ([color=BLUE]setq[/color] en ([color=BLUE]entget[/color] en) co ([color=BLUE]abs[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 62 en))) ) ([color=BLUE]entmod[/color] ([color=BLUE]subst[/color] ([color=BLUE]cons[/color] 62 ([color=BLUE]if[/color] flag co ([color=BLUE]-[/color] co))) ([color=BLUE]assoc[/color] 62 en) en)) ) ) ) This is the method I was trying to explain in my previous post. One more method... ;; layer - name of layer ;; flag - t=ON, nil=OFF (defun vl-LayerOnOff2 (layer flag / obj) (if (setq obj (tblobjname "LAYER" layer)) (vla-put-layeron (vlax-ename->vla-object obj) (if flag :vlax-true :vlax-false ) ) ) ) Leave it to Alan to come up with a way to make it even more efficient and to remove the need for error handling! Quote
Lee Mac Posted January 10, 2012 Posted January 10, 2012 Another benefit of this is you could then use it on ObjectDBX documents - i.e. editing DWG files without opening them. ObjectDBX compatibility is always a good thing to incorporate. even more efficient AFAIK, Vanilla AutoLISP is the fastest way to manipulate Table & Graphical entities, the conversion from Vanilla to Visual is inefficient - this is reflected when iterating over SelectionSets also. Quote
alanjt Posted January 10, 2012 Posted January 10, 2012 AFAIK, Vanilla AutoLISP is the fastest way to manipulate Table & Graphical entities, the conversion from Vanilla to Visual is inefficient - this is reflected when iterating over SelectionSets also. Entmod'ing the layer is fastest way. I was just offering another alternative since both sides of the spectrum were be tackled. Quote
irneb Posted January 10, 2012 Posted January 10, 2012 I beg to disagree: Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (RK:LAYEROFF)...........1641 / 1.72 <fastest> (LM:VLA:LAYEROFF).......1781 / 1.59 (ALANJT:LAYEROFF).......1891 / 1.50 (LM:DXF:LAYERONOFF).....2438 / 1.16 (THARWAT:LAYEROFF)......2828 / 1.00 <slowest> Here's the code I used for the test: (setq acadobject (vlax-get-Acad-Object)) (setq activedocument (vla-get-activedocument acadobject)) (setq LayerTable (vla-get-layers activedocument)) (defun RK:LayerOff (/) (vla-put-layeron (vlax-invoke-method LayerTable "Item" "A-Open springs") :vlax-false) ) (defun Tharwat:LayerOff (/) (vla-put-layeron (vla-item (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))) "A-Open springs") :vlax-false ) ) (defun LM:dxf:LayerOnOff (/ co en) (if (setq en (tblobjname "LAYER" "A-Open springs")) (progn (setq en (entget en) co (abs (cdr (assoc 62 en))) ) (entmod (subst (cons 62 (- co)) (assoc 62 en) en)) ) ) ) (defun LM:vla:LayerOff (/ obj) (if (null (vl-catch-all-error-p (setq obj (vl-catch-all-apply 'vla-item (list LayerTable "A-Open springs")))) ) (not (vla-put-layeron obj :vlax-false)) ) ) (defun alanjt:LayerOff (/ obj) (if (setq obj (tblobjname "LAYER" "A-Open springs")) (vla-put-layeron (vlax-ename->vla-object obj) :vlax-false) ) ) Both RK's and LM's vla methods are the same speed - though LM's is a slight bit slower due to the added error handling. Basically they do the same thing. However Tharwat's is the slowest as it needs to obtain the acad, document & layers collection each time. But the dxf method of LM's is only very slightly faster than Tharwat's. If you don't have the layer collection already then alanjt's is the fastest. The reason the entmod is so slow is due to the entire list being obtained from entget and then modified through subst with 2 times an assoc. And then the entmod gets the entire list again. Quote
Lee Mac Posted January 10, 2012 Posted January 10, 2012 I would argue that Tharwat's and RK's code cannot be compared since without the required error trapping, you are no longer comparing apples with apples. Quote
Lee Mac Posted January 10, 2012 Posted January 10, 2012 These are my results: Functions unaltered, except for function name: ;; layer - name of layer ;; flag - t=ON, nil=OFF (defun LM:DXF:LayerOnOff ( layer flag / co en ) (if (setq en (tblobjname "LAYER" layer)) (progn (setq en (entget en) co (abs (cdr (assoc 62 en))) ) (entmod (subst (cons 62 (if flag co (- co))) (assoc 62 en) en)) ) ) ) ;; layers - Layers Collection ;; layer - name of layer ;; flag - t=ON, nil=OFF (defun LM:VLA:LayerOnOff ( layers layer flag / obj ) (if (null (vl-catch-all-error-p (setq obj (vl-catch-all-apply 'vla-item (list layers layer)) ) ) ) (not (vla-put-layeron obj (if flag :vlax-true :vlax-false))) ) ) ;; layer - name of layer ;; flag - t=ON, nil=OFF (defun AT:VLA:LayerOnOff (layer flag / obj) (if (setq obj (tblobjname "LAYER" layer)) (vla-put-layeron (vlax-ename->vla-object obj) (if flag :vlax-true :vlax-false ) ) ) ) Results: _$ (setq aclay (vla-get-layers (vla-get-activedocument (vlax-get-acad-object)))) #<VLA-OBJECT IAcadLayers 0f5533dc> _$ _$ (Benchmark '((LM:DXF:LayerOnOff "0" nil) (LM:VLA:LayerOnOff aclay "0" nil) (AT:VLA:LayerOnOff "0" nil))) Benchmarking ................Elapsed milliseconds / relative speed for 8192 iteration(s): (LM:DXF:LAYERONOFF "0" nil)...........1092 / 1.04 <fastest> (LM:VLA:LAYERONOFF ACLAY "0" nil).....1108 / 1.03 (AT:VLA:LAYERONOFF "0" nil)...........1139 / 1 <slowest> Quote
alanjt Posted January 10, 2012 Posted January 10, 2012 These are my results:Results: _$ (setq aclay (vla-get-layers (vla-get-activedocument (vlax-get-acad-object)))) #<VLA-OBJECT IAcadLayers 0f5533dc> _$ _$ (Benchmark '((LM:DXF:LayerOnOff "0" nil) (LM:VLA:LayerOnOff aclay "0" nil) (AT:VLA:LayerOnOff "0" nil))) Benchmarking ................Elapsed milliseconds / relative speed for 8192 iteration(s): (LM:DXF:LAYERONOFF "0" nil)...........1092 / 1.04 <fastest> (LM:VLA:LAYERONOFF ACLAY "0" nil).....1108 / 1.03 (AT:VLA:LAYERONOFF "0" nil)...........1139 / 1 <slowest> That's how I thought it would work. Of course, with the time difference being so minute, it really doesn't matter. For giggles, I wonder how bad the comparison would be if you tested with a command call. Quote
irneb Posted January 10, 2012 Posted January 10, 2012 Strange! All I can think of is that the PC/OS/Version has an effect. I'm running on Vanilla 2011 on WinXP-SP3-32bit, Core 2 - 6600 @ 2.4GHz & 3GB RAM. If I run Lee's exact code I get: Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (LM:VLA:LAYERONOFF ACLAY "0" nil)......1844 / 20.74 <fastest> (AT:VLA:LAYERONOFF "0" nil)............1922 / 19.90 (LM:DXF:LAYERONOFF "0" nil)............2843 / 13.45 (CMD:LAYERONOFF "0" nil)..............38250 / 1.00 <slowest> Added the command call version: (defun cmd:LayerOnOff (layer flag / co c en) (command "._LAYER" (if flag "_ON" "_OFF") layer) (if (eq (strcase layer) (strcase (getvar 'CLayer))) (command "_Yes")) (command "") ) As expected it's STUPIDly slow And to get better results I omitted the command line version: Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (LM:VLA:LAYERONOFF ACLAY "0" nil).....1828 / 1.40 <fastest> (AT:VLA:LAYERONOFF "0" nil)...........1921 / 1.33 (LM:DXF:LAYERONOFF "0" nil)...........2562 / 1.00 <slowest> Not much difference at all. If I think about it, it might be that ActiveX is not as efficient on 64bit systems. It means there's some conversion between 32 & 64 bit. Thus the ActiveX is only faster when running on 32bit. Quote
Lee Mac Posted January 10, 2012 Posted January 10, 2012 That's how I thought it would work. Of course, with the time difference being so minute, it really doesn't matter. Ditto, since we've seen in the past the inefficiency of vlax-ename->vla-object; but I agree, on my machine the difference is negligible. Strange! All I can think of is that the PC/OS/Version has an effect. I'm running on Vanilla 2011 on WinXP-SP3-32bit, Core 2 - 6600 @ 2.4GHz & 3GB RAM. Here are my specs: W7-Ultimate SP1 32-bit Core 2 @ 2.10GHz & 3GB RAM If I run Lee's exact code I get:Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (LM:VLA:LAYERONOFF ACLAY "0" nil)......1844 / 20.74 <fastest> (AT:VLA:LAYERONOFF "0" nil)............1922 / 19.90 (LM:DXF:LAYERONOFF "0" nil)............2843 / 13.45 (CMD:LAYERONOFF "0" nil)..............38250 / 1.00 <slowest> Added the command call version: (defun cmd:LayerOnOff (layer flag / co c en) (command "._LAYER" (if flag "_ON" "_OFF") layer) (if (eq (strcase layer) (strcase (getvar 'CLayer))) (command "_Yes")) (command "") ) As expected it's STUPIDly slow Wow - I knew the command version would be slower, but not that much slower! I wonder if using "LAYER" instead of "-LAYER" has increased the time? Quote
Tharwat Posted January 10, 2012 Posted January 10, 2012 Would this be of any interests ? ; layername = layer name to be on / off ; flag = "on" or "off" (defun VLAPutLayerOff (layername flag) (cond ((not acdoc) (setq acdoc (vla-get-activedocument (vlax-get-acad-object))) ) ) (cond ((not lays) (setq lays (vla-get-layers acdoc)) ) ) (if (tblsearch "LAYER" layername) (cond ((eq (strcase flag) "ON") (vla-put-layeron (vla-item lays layername) :vlax-true) ) ((eq (strcase flag) "OFF") (vla-put-layeron (vla-item lays layername) :vlax-false) ) (t (princ "\n Bad flag dear user !!!") ) ) (princ "\n Layer name is not found in current drawing") ) (princ) ) Quote
alanjt Posted January 10, 2012 Posted January 10, 2012 Man, command is sllllllllllooooooooooooowwwwwww! Quote
irneb Posted January 10, 2012 Posted January 10, 2012 Tharwat, the idea is good ... the problem is the cond's are adding a bit, also that you're now introducing string comparison as well, but worse the tblsearch obtains the dxf data list instead of the tblobjname which only gets the ename. As sample I've changed yours to use a T/nil flag, simplified the cond's into one or and removed all the princ's: (defun IB:LayerOnOff (layername flag / ) (or lays (setq lays (vla-get-layers (vla-get-ActiveDocument (vlax-get-acad-object))))) (if (tblsearch "LAYER" layername) (vla-put-layeron (vla-item lays layername) (if flag :vlax-true :vlax-false)) ) ) Yet still it's rather slow due to the tblsearch: Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (LM:VLA:LAYERONOFF ACLAY "0" nil).....1844 / 3.90 <fastest> (AT:VLA:LAYERONOFF "0" nil)...........1922 / 3.74 (LM:DXF:LAYERONOFF "0" nil)...........2578 / 2.79 (IB:LAYERONOFF "0" nil)...............6375 / 1.13 (VLAPUTLAYEROFF "0" "OFF")............7188 / 1.00 <slowest> Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.