Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Modifying lisp to save layer state before changing layer properties.

15 REPLIES 15
Reply
Message 1 of 16
cad77
1269 Views, 15 Replies

Modifying lisp to save layer state before changing layer properties.

Hello,

The following routine is my attempt to

1. Test to see if the layer state "old" exists in the current drawing.
2. If so, delete it.
3. Create another layer state with the name "old".
4. Continue with the routine to change layer properties.

Here it is:

(defun c:SWL ()
;
(if (defun get-layerstate (OLD)
(if (vl-position
(strcase name)
(get-statenames))
name))
(command "-layer" "state" "delete" "OLD")
)
(command "-layer" "state" "save" "OLD" "" "" "")
;
;Thaw a list of layers:
(command "-layer" "thaw" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO,SURV_PNTS_WATER"
;Create a layer:
"make" "SURV_PNTS_WATER" "color" "white" "" "ltype" "continuous" ""
;Turn off all layers except the current layer:
"off" "*" "no"
;Turn on a list of layers:
"on" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO" "")
(princ)
)

Some of the code I'm using is from Luis Esquivel's post at: http://discussion.autodesk.com/thread.jspa?messageID=1149694 .

The routine is not performing as I want it to. If the layer state "old" doesn't exist, I'm receiving a lot of command line messages "Unknown layer state name" - and then it appears to finish the routine alright. If the layer state "old" does exist, I'm receiving "Invalid option keyword" - and then the routine stops. Any suggestions?

Thanks for any help,

CAD77
15 REPLIES 15
Message 2 of 16
Tom Smith
in reply to: cad77

Don't have Acad here to test, but here are some observations.

a) get-statenames) isn't defined. Presumably it's defined elsewhere.

b) Convoluted if tests make my eyes hurt. This one can't work, because the defun returns "something" (namely the newly defined function name) therefore your if test boils down to (if t ... ). Define get-layerstate before you try to use it, then call it and use its return value as a test.

c) Due to the above, as written, the routine always tries to delete "old."

d) Small point, but I'd give get-layerstate a more meaningful name. If it's purpose is to save an old layer state, I'd call it save-old-layer-state.

e) If you're going to use layer states to save an old condition, why in the world use layer commands to bring about a new one? Just import and restore a saved layer state. Look at the options. You can optionally control color, linetype, on or off, freezing or thawing, or optionally turn off existing layers which aren't saved in the state. Pretty much anything you could want. Plus, once you've done it, a non-programmer can update or change the state in the future, without wading through the layer command sequence in a lisp.

f) Same rules apply. You can't import a layer state whose name exists, so you have to delete it first. The method has been discussed and posted here before.
Message 3 of 16
cad77
in reply to: cad77

Hi Tom,

Okay, I've been working on this to no avail - so far.

Anyway, I've been looking at page 8-9 of the book "AutoLISP Programming Principles and Techniques" about "Creating Simple Branches With If". So far, my attempt at an if/then/else statement is:

(defun OLDTEST (OLD/CREATE)
(if
(get-layerstate (OLD))
(princ (setq CREATE (command "-layer" "state" "delete" "OLD" "save" "OLD" "" "" "")))
(princ "\nLayer state 'OLD' does not exist.")
)
(princ)
)

I'm not sure what it's doing wrong, but nothing's happening with the layer states.

Anyway, per your observation 'a', I also included the rest of Luis' lisp, which is lengthy (I had thought I could use a piece of it). I also modified the "get-layerstate" name to "save-old-layer-state." It is as follows (with my "OLD" replacing Luis' "TEST"):

(vl-load-com)

(defun get-acadobject ()
(setq *acad*
(cond
(*acad*)
((vlax-get-acad-object))
(T nil))))

(defun get-activedocument ()
(setq *doc*
(cond
(*doc*)
((vla-get-activedocument (get-acadobject)))
(T nil))))

(defun delete-layerstate (obj name)
(if (vl-catch-all-error-p
(vl-catch-all-apply
'vla-delete
(list obj name)))
nil
T))

(defun save-layerstate (obj name mask)
(if (vl-catch-all-error-p
(vl-catch-all-apply
'vla-save
(list obj name mask)))
nil
T))

(defun set-layerstate (name mask)
(setq state
(vlax-create-object
"AutoCAD.AcadLayerStateManager"))
(vla-setdatabase
state
(vla-get-database (get-activedocument)))
(delete-layerstate state name)
(save-layerstate
state
name
;; acLsAll
mask))

(defun save-old-layer-state (/ collection names)
(if (not
(vl-catch-all-error-p
(setq collection
(vl-catch-all-apply
(function (lambda ()
(vla-item (vla-getextensiondictionary
(vla-get-layers
(get-activedocument)))
"ACAD_LAYERSTATES")))))))
(vlax-for item collection
(setq names (cons (strcase (vla-get-name item)) names)))))

(defun restore-layerstate (name)
(if (and state
(vl-position
(strcase name)
(save-old-layer-state)))
(progn (vla-restore state name)
(delete-layerstate state name)
(vlax-release-object state)
(setq state nil))))

;; (get-layerstate "OLD")
(defun get-layerstate (name)
(if (vl-position
(strcase name)
(save-old-layer-state))
name))

I'm not getting an error message using this, it's just that nothing's happening to the layer states - so I'm not sending the right message to AutoCAD.

Anyway, I could use some help with if/then/else statements.

Thanks,

CAD77
Message 4 of 16
Anonymous
in reply to: cad77

While I haven't studied your code or intent, if you are using
AutoCAD 2008 you might have a look at the new built-in
layerstate functions.



--
Autodesk Discussion Group Facilitator


wrote in message news:5799853@discussion.autodesk.com...
Hello,

The following routine is my attempt to

1. Test to see if the layer state "old" exists in the current drawing.
2. If so, delete it.
3. Create another layer state with the name "old".
4. Continue with the routine to change layer properties.
Message 5 of 16
cad77
in reply to: cad77

Jason,

We are using 2005 here, so I can't look at the new features. Hopefully, one of these days . . .

Anyway, I tried to get an if/then statement working for about 1-2 hours this morning, to no avail.

I decided to try writing the routine to immediately delete a layer state "old", whether or not it existed. Then save a new layer state with the same name. It appears to work, except that I get an annoying command window pop-up each time that has to be closed.

Here's what I've got:

(defun c:SWL ()

(setvar 'cmdecho 1)

;Delete the layer state "OLD".
(command "-layer" "state" "delete" "OLD" "" "" "")

;Create the layer state "OLD".
(command "-layer" "state" "save" "OLD" "" "" "")

;Thaw a list of layers:
(command "-layer" "thaw" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO,SURV_PNTS_WATER"

;Create a layer:
"make" "SURV_PNTS_WATER" "color" "white" "" "ltype" "continuous" ""

;Turn off all layers except the current layer:
"off" "*" "no"

;Turn on a list of layers:
"on" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO" "")

(setvar 'cmdecho 0)

(princ)
)

I'd still like to test to see if "old" exists before deleting it; but I can't figure that out. Also, I'd like to prevent the command window pop-up from appearing.

Any help would be appreciated.

CAD77
Message 6 of 16
Anonymous
in reply to: cad77

I've not worked much with layerstates, so I don't think I have
any canned code to do what you want. As for the command
window, do you mean the text screen? Try the (graphscr) and
(textscr) functions.


--
Autodesk Discussion Group Facilitator


wrote in message news:5800404@discussion.autodesk.com...

> I'd still like to test to see if "old" exists before deleting it; but
> I can't figure that out. Also, I'd like to prevent the command
> window pop-up from appearing.
Message 7 of 16
cad77
in reply to: cad77

Jason,

I've added the (graphscr) function to the routine. All I see now when I run the routine is a flash of the text screen, and then it disappears. Thanks for the tip. Here's what I've got now:

(defun c:SWL ()

(setvar 'cmdecho 1)

;Delete the layer state "OLD".
(command "-layer" "state" "delete" "OLD" "" "" "")

;Create the layer state "OLD".
(command "-layer" "state" "save" "OLD" "" "" "")

;Remove the text screen.
(graphscr)

;Thaw a list of layers:
(command "-layer" "thaw" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO,SURV_PNTS_WATER"

;Create a layer:
"make" "SURV_PNTS_WATER" "color" "white" "" "ltype" "continuous" ""

;Turn off all layers except the current layer:
"off" "*" "no"

;Turn on a list of layers:
"on" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO" "")

(setvar 'cmdecho 0)

(princ)
)

I would still appreciate anyone's help with eliminating the need to run the delete layer state command if the layer state "old" doesn't exist.

Thanks again,

CAD77
Message 8 of 16
Tom Smith
in reply to: cad77

Here's what I'd do. Less sophisticated than Luis' code but it works.

First I'd adapt the thing to import the desired layer state too. That means you first have to delete any state that already has the same name, same as you do when you save a layer state. Also, you have to make sure any linetypes in the layer state are present. Easiest way to do that is load every standard linetype you use.

;include all needed ltypes
(defun load-ltypes ()
(setvar "filedia" 0)
(foreach n '("center" "dashed" "hidden" "phantom")
(if (null (tblsearch "ltype" n))
(command "-linetype" "load" n "acad.lin" "")))
(setvar "filedia" 1)
(princ))

(defun delete-lstate (name)
(command "-layer" "state" "delete" name "" "")
(if (not (zerop (getvar "cmdactive")))
(command "")) ;extra return if lstate not found)

(defun save-lstate (name)
(delete-lstate name)
(command "layer" "state" "save" name "" "" "" ""))

;assumes that state "name" is in a file named "name.las" on the search path
(defun import-lstate (name / lasfile)
(load-ltypes)
(delete-lstate name)
(setq lasfile (strcat name ".las"))
(if (findfile lasfile)
(command "layer" "state" "import" lasfile "restore" name "" "")))

(defun c:test ()
(command "undo" "end")
(command "undo" "begin")
(save-lstate "old")
(import-lstate "new")
(command "undo" "end")
(princ))

Note there's no need to test for existence before deleting a layer state. You just need to provide an extra return if the name wasn't found.

To figure out things like that, turn cmdecho on so you can see the prompt sequence after the routine runs.

The test function assumes you've created and exported a layer state named "new" which has everything the way you want it, and it's in a file named "new.las."

I disapprove of names like new and old. That's just for illustrative purposes. A better approach would to call your standard layers something like "standard-layers.las" if that's what they are. Instead of old, I'd string together a state name based on the date it was being saved.
Message 9 of 16
EC-CAD
in reply to: cad77

Atop of code, add this function..
(defun get-old-layer-states (/ collection names)
(vl-load-com)
(setq names (list))
(if (not
(vl-catch-all-error-p
(setq collection
(vl-catch-all-apply
(function (lambda ()
(vla-item (vla-getextensiondictionary
(vla-get-layers
(get-activedocument)))
"ACAD_LAYERSTATES")))))))
(vlax-for item collection
(setq names (cons (strcase (vla-get-name item)) names))
))
)

Then, change the sequence to:
;; get a list of existing layer states..
(get-old-layer-states)

;; Delete the layer state "OLD" (if it exists).
(if (member "OLD" names)
(command "-layer" "state" "delete" "OLD" "" "" "")
); if

Interesting, posting added a space in ACAD. Remove that.

Bob Message was edited by: ECCAD
Message 10 of 16
Tom Smith
in reply to: cad77

Bob, see my post above. The way I've handled the same thing is to just go ahead and delete the layer state without checking for it first. The only difference is that the layer command takes one more return if the name wasn't found, and the cmdactive trick will take care of that. Not as elegant, but less code and it works.
Message 11 of 16
cad77
in reply to: cad77

Tom,

Thanks for the help with the routine.

I've been reading through your code, and I think I understand what it's doing.

Maybe if I try to explain it here you can tell me what I'm not understanding? Here goes:

1. It defines a function "load-ltypes" that will load missing linetypes from the acad.lin file. Q: does it only load the linetypes "center", "dashed", "hidden", and "phantom"?

2. It defines a function "delete-lstate" with the argument "name." This function deletes the layer state "name" if it is found and issues an extra return if it isn't.

3. It defines a function "save-lstate" that also has the argument "name." This function deletes the layer state "name" (if found) and saves the current layer state with the name "name."

4. It defines a function "import-lstate" with the argument "name" and the local variable "lasfile". This function imports the layer state "name".las (if found) and restores it.

5. It defines the command "test" which deletes the layer state "old", replaces it with a new "old" and imports the layer state "new." It also places undo begin and end marks to allow for one undo.

Am I on the right track?

I've tried putting your code into notepad and running it with the command "test", but it says "malformed list on input" and Unknown command "TEST". Do I need to combine it with part of my code?

Regarding having a layer state already saved and importing it: what if there are layers in the current drawing unaddressed by the layer state manager? Won't they show up? I was thinking that by running the -layer command and only turning on a list of layers, it would eliminate the need to deal with new or oddly named layers that would show up after importing a layer state. Maybe I'm not understanding properly how the state manager works?

Also, I had thought of using a state with the current date; but my supervisor and I were thinking that the problem would be that we'd run into a long list of layer states with old dates - instead of there just being one old state that's constantly being updated. We're planning on putting this layer state feature at the front of many routines that will modify layer properties.

Thanks again for your help,

CAD77
Message 12 of 16
cad77
in reply to: cad77

Bob,

Thanks for the post.

I've added your code to my routine, and am finding one problem: when I already have a layer state named "OLD" in the drawing, it is not being replaced by a newer state with the same name. I am doing the following:

1. Start a new drawing without a layer state "OLD".
2. Loading the routine.
3. Running the routine.
4. Checking to see that "OLD" exists.
5. Changing the layer properties (mainly thawing and turning on all layers).
6. Running the routine again.
7. Restoring the state "OLD".
8. I end up with the original "OLD" layer state. "OLD" wasn't replaced.

Am I doing something wrong?

Thanks,

CAD77

P.S. Here's the routine:

(defun get-old-layer-states (/ collection names)
(vl-load-com)
(setq names (list))
(if (not
(vl-catch-all-error-p
(setq collection
(vl-catch-all-apply
(function (lambda ()
(vla-item (vla-getextensiondictionary
(vla-get-layers
(get-activedocument)))
"ACAD_LAYERSTATES")))))))
(vlax-for item collection
(setq names (cons (strcase (vla-get-name item)) names))
))
)

(defun c:SWL ()

(setvar 'cmdecho 1)

;; get a list of existing layer states..
(get-old-layer-states)

;; Delete the layer state "OLD" (if it exists).
(if (member "OLD" names)
(command "-layer" "state" "delete" "OLD" "" "" "")
); if

(command "-layer" "state" "save" "OLD" "" "" "")

;Remove the text screen.
(graphscr)

;Thaw a list of layers:
(command "-layer" "thaw" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO,SURV_PNTS_WATER"

;Create a layer:
"make" "SURV_PNTS_WATER" "color" "white" "" "ltype" "continuous" ""

;Turn off all layers except the current layer:
"off" "*" "no"

;Turn on a list of layers:
"on" "*WATER*,*ROAD*,*BND*,*CURB*,*GUTTER*,*SIDEWALK*,XREF,*LOT,*BND_PHASE*,*LOT_NO" "")

(setvar 'cmdecho 0)

(princ)
)
(prompt
"Water_Survey lisp: to set up layers properly for survey water stakeout. Enter 'SWL' to start routine."
)


P.S.S. I don't have a space between "AC..." and "...AD_LAYERSTATES". If it looks like in this post, it's being added.
Message 13 of 16
EC-CAD
in reply to: cad77

Try moving:
(command "-layer" "state" "save" "OLD" "" "" "")
.. to just past thawing of layers..

Bob
Message 14 of 16
Tom Smith
in reply to: cad77

Yes on all points.

>"malformed list on input"

You missed a parenthesis, that's what that error always means. I copied and pasted it from here with no problem.

>Do I need to combine it with part of my code?

No. This is all you need. It's not as sophisticated as Luis' stuff, but it's a lot shorter and simpler.

>what if there are layers in the current drawing unaddressed by the layer state manager? Won't they show up?

Like I said, you can choose whether or not to turn off layers that aren't in the state, among other things. For instance you can choose whether or not the freeze/thaw status is part of the saved state. There's nothing in the layer state that you don't put there. Run the command and see for yourself.

Start with a drawing that contains nothing but the layers you want. If you save that state with the "Turn off layers not found in layer state" box checked, then when you import that stae, nothing but your desired layers will be turned on.

When you export the LAS file, make sure it's located on your search path.
Message 15 of 16
cad77
in reply to: cad77

Tom,

I found the error with the parenthesis and have been able to test the routine successfully.

Thanks again for your help with the code.

CAD77
Message 16 of 16
cad77
in reply to: cad77

Bob,

I'll give it a shot. Thanks again for your help,

CAD77

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost