Lisp routine - change layer phase

Lisp routine - change layer phase

scott_clingan9H77F
Participant Participant
782 Views
15 Replies
Message 1 of 16

Lisp routine - change layer phase

scott_clingan9H77F
Participant
Participant

For starters i know nothing about LISP.  So I turned to ChatGPT to try and help me with a task. I want to be able to select elements in a drawing and change their layer to specific phase (ie. New, Existing, Removal). ChatGPT gave me a great looking script, but I keep getting errors when I try to debug it. Debugging is also something I know nothing about. 

Currently the routine is hanging up on line 10 with error:

Exception has occurred.
bad argument type: lentityp (3 <Entity name: 200561f28f0> 0 -1)



 

 

(defun c:cx (/ ss newPhaseStatus)
  (setq ss (ssget)) ; Prompt the user to select objects
  
  (if (not ss)
    (prompt "\nNo objects selected.")
    (progn
      (setq newPhaseStatus (strcase (getstring "\nEnter 'E' for Existing Conditions, 'R' for Removal Work, or 'N' for New Work: ")))
      (if (or (= newPhaseStatus "E") (= newPhaseStatus "R") (= newPhaseStatus "N"))
        (progn
          (foreach obj (vl-remove-if-not 'vlax-ename->vla-object (mapcar 'vlax-ename->vla-object (ssnamex ss)))
            (vl-catch-all-apply
              '(lambda ()
                 (setq currentLayer (vla-get-Layer obj))
                 (setq layerParts (split-string currentLayer "-"))
                 
                 (if (< (length layerParts) 4)
                   (setq layerParts (append layerParts (list ""))))
                 
                 (setq newLayerName (strcat (nth 0 layerParts) "-" (nth 1 layerParts) "-" (nth 2 layerParts) "-"))
                 (setq newLayerName (strcat newLayerName newPhaseStatus))
                 
                 (if (not (tblsearch "layer" newLayerName))
                   (progn
                     (setq layerColor (getcolor "\nEnter layer color (0-255): "))
                     (setq lineType (if (= newPhaseStatus "E") "Continuous" "Hidden"))
                     
                     (entmakex
                       (list
                         '(0 . "LAYER")
                         '(100 . "AcDbSymbolTableRecord")
                         '(100 . "AcDbLayerTableRecord")
                         (cons 2 newLayerName)
                         (cons 70 0)
                         (cons 62 layerColor)
                         (cons 6 lineType)
                       )
                     )
                   )
                 )
                 
                 (vla-put-Layer obj newLayerName)
               ))
          )
          (prompt "\nLayers updated successfully.")
        )
        (prompt "\nInvalid input. Please enter 'E', 'R', or 'N'."))))
  (princ)
)

 

 

 

0 Likes
783 Views
15 Replies
Replies (15)
Message 2 of 16

paullimapa
Mentor
Mentor

Instead of looking at that code perhaps you can post a sample dwg with typical layers already created for each phase and then describe in detail when an object is selected and then given N, E or R how would you determine exactly which of the multitude of layers found in each category do you want this particular selected object to be changed to. 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 3 of 16

MrJSmith
Advocate
Advocate

As someone who routinely uses ChatGPT, it is EXTREMELY limited in its capabilities when it comes to autolisp. More frequently used languages like python, JS, powershell, etc it does really well with given its vast data on those it has available. For AutoLISP, it will just make up functions or do things you can't do in the language.

 

In your specific case, it was attempting to use a "shortcut method" to turn a selection set into a LIST. It was close but messed up at the end. It should have been something like:

 

(foreach e (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))
     (setq obj (vlax-ename->vla-object e))
     ......

 

Or if you wanted to do it all in one line it was attempting.....

(foreach obj (mapcar 'vlax-ename->vla-object (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss ))))

 

Note: These "shortcut" methods are a bit slower then other traditional methods so if you have a huge amount of entities your selection (talking like 10k to 100k+ ranges) then it might be better to use a different method for the conversion.

0 Likes
Message 4 of 16

john.uhden
Mentor
Mentor

@MrJSmith , @paullimapa :

Is this any better?  (Sorry about having had capslock on)

(setq objects (MAPCAR 'VLAX-ENAME->VLA-OBJECT (MAPCAR 'CADR (VL-REMOVE-IF-NOT '(LAMBDA (X)(= (CAR X) 3)) (SSNAMEX SS)))))

Before I just learned about ssnamex, I would have used:

(defun ss2list (ss / i lst)
  (repeat (setq i (sslength ss))
    (setq lst (cons (ssname ss (setq i (1- i))) lst))
  )
)
;; Then
(defun ss2objlist (ss)
  (mapcar 'vlax-ename->vla-object (ss2list ss))
)

 ( 

John F. Uhden

0 Likes
Message 5 of 16

paullimapa
Mentor
Mentor

To compare apples to apples, I think this:

(mapcar 'vlax-ename->vla-object (ss2list ss))

needs this:

(setq objects (mapcar 'vlax-ename->vla-object (ss2list ss)))

To 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 6 of 16

john.uhden
Mentor
Mentor

@paullimapa 

That would be correct if you wanted to set objects as a global within the function.

My idea is almost always just to create a function that returns the desired result, as in

(setq objects (ss2objlist ss)).

In fact you may not need to set objects at all, as in

(foreach obj (ss2objlist ss)

  (do_something_to obj)

)

John F. Uhden

0 Likes
Message 7 of 16

paullimapa
Mentor
Mentor

O really going off topic now but perhaps have 2 functions:

(1) that makes a list but keeps as entity

(2) another converts them into vl objects

For the vl object list you can use (vlax-dump-object obj) to see all the property names & then use vla-get & vla-put.

For the entity list you can use (dumpallproperties en) to see all the property names & then use  

getpropertyvalue & setpropertyvalue

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 8 of 16

john.uhden
Mentor
Mentor

@paullimapa ,

(1) and (2) are exactly what I provided in message 4 of 8.

What the OP wants to do with them after is up to him/her.

I say him/her because when Laurie Comerford showed up years ago, most participants thought he was a girl.

Upon his visit to me from Australia, it was kinda obvious that he was not a she.

I've got a picture of him and me saved somewhere, but not on this work laptop.  But it is also somewhere here in this forum's history.

FOUND IT... We're on the patio in front of our porch with one of my wife's beautiful hanging geraniums (gerania?) behind.

johnuhden_0-1695596116123.jpeg

 

John F. Uhden

0 Likes
Message 9 of 16

Kent1Cooper
Consultant
Consultant

One problem is that there are no definitions of the (split-string) or (getcolor) functions, which are not native AutoLisp functions, and so would need to be defined.  But I don't think they're needed, if I'm reading what it thinks it's trying to do correctly.  If the idea is [there's some weird enough stuff in there to make me doubtful, but...] to put the selected objects on Layers with the same name as their current Layers plus a hyphenated suffix letter for the phase, and with a color chosen for the phase, then I think it can be something like this [minimally tested]:

(defun C:COPSL ; Change Object(s) to Phase-Suffixed Layer(s)
  (/ ss phase clr n ent lyr)
  (if (setq ss (ssget "_:L")); nothing on locked Layer(s)
    (progn ; then
      (initget 1 "Existing Removal New"); no Enter
      (setq
        phase (getkword "\nPhase [Existing/Removal/New]: ")
        clr (acad_colordlg 7 nil); black/white initial default; no ByBlock/ByLayer
      ); setq
      (repeat (setq n (sslength ss))
        (setq
          ent (ssname ss (setq n (1- n)))
          lyr (strcat (cdr (assoc 8 (entget ent))) "-" (substr phase 1 1))
        ); setq
        (if (not (tblsearch "layer" lyr))
          (command "_.layer"
            "_new" lyr "_color" clr lyr
            "_ltype" (if (= phase "Existing") "continuous" "hidden") lyr
            ""
          ); command
        ); if
        (command "_.chprop" ent "" "_layer" lyr "")
      ); repeat
    );progn
  ); if
  (prin1)
)

It uses some things ChatGPT apparently doesn't know about yet -- (initget)/(getkword) to offer the phase options and prevent invalid entry in the process, (acad-colordlg) to let you pick the color in the color chart instead of needing to know what color number you want.  And it doesn't require converting things to VLA objects.

 

It could use some enhancements that I don't see in the ChatGPT code, either.  What if something is already on a Layer with that kind of suffix?  Should it leave it alone if it's the one selected?  Or even if it's one of the others?  If it's one of the others, should it change that to the current option, or tag on a second suffix?  Would there be a standard for the colors of the phases, so they could be built in instead of asking the User?  Etc.

 

If I've misunderstood the intent, a more detailed description would be in order.

Kent Cooper, AIA
Message 10 of 16

Kent1Cooper
Consultant
Consultant

@paullimapa wrote:

....

For the entity list you can use (dumpallproperties en) to see all the property names & then use  

getpropertyvalue & setpropertyvalue

Unfortunately, that's more complicated than one would expect in regard to an object's Layer.  There's no "Layer" property that's just a text-string Layer name you can get from something with (getpropertyvalue) or apply to it with (setpropertyvalue).  The property is called "LayerID", and it's the Layer table object of the Layer, not just the name.  So, for instance, to get the Layer name of the last entity using (getpropertyvalue) methods, you need to do this:

(cdr (assoc 2 (entget (getpropertyvalue (entlast) "LayerID"))))
And to assign Layer "0" to the last entity, you need to do this:
(setpropertyvalue (entlast) "LayerID" (tblobjname "layer" "0"))

Kent Cooper, AIA
0 Likes
Message 11 of 16

paullimapa
Mentor
Mentor

Yes, thanks for pointing that out @Kent1Cooper. It does look like another level of complexity introduced with getpropertyvalue for getting the Layer name.

But still you can use the same function again to get that as well like this:

(getpropertyvalue (getpropertyvalue (entlast) "LayerID") "Name")

So many ways to skin a ....


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 12 of 16

john.uhden
Mentor
Mentor

@Kent1Cooper & @paullimapa ,

I guess that's why I use neither (getpropertyvalue) nor (setpropertyvalue).

I stick to the older object method...

 

Command: (vlax-get object 'layer)
"V-STM-DIVIDE"

John F. Uhden

0 Likes
Message 13 of 16

MrJSmith
Advocate
Advocate

@john.uhden wrote:

@MrJSmith , @paullimapa :

Is this any better?  (Sorry about having had capslock on)

 

(setq objects (MAPCAR 'VLAX-ENAME->VLA-OBJECT (MAPCAR 'CADR (VL-REMOVE-IF-NOT '(LAMBDA (X)(= (CAR X) 3)) (SSNAMEX SS)))))

 

Before I just learned about ssnamex, I would have used:

 

(defun ss2list (ss / i lst)
  (repeat (setq i (sslength ss))
    (setq lst (cons (ssname ss (setq i (1- i))) lst))
  )
)
;; Then
(defun ss2objlist (ss)
  (mapcar 'vlax-ename->vla-object (ss2list ss))
)

 

 ( 


As I mentioned, ssnamex is slower compared to the ss2list function. I stopped using it because of that reason and went back to either the repeat loop or the while loop with ssname.

0 Likes
Message 14 of 16

paullimapa
Mentor
Mentor

fyi, for some reason I could not get this version of your code to work:

(setq objects (mapcar 'vlax-ename->vla-object (mapcar 'cadr (vl-remove-if-not '(lambda (x)(= (car x) 3)) (ssnamex ss)))))

I ended up use this version which works for me:

(setq objects (mapcar 'vlax-ename->vla-object (vl-remove-if 'listp (mapcar 'cadr (ssnamex ss)))))

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 15 of 16

scott_clingan9H77F
Participant
Participant

Thanks for all the input. Like I said, I'm pretty useless when it comes to programing. I'll go through your suggestions and see if I can get any of it to do what I'm looking for. 

0 Likes
Message 16 of 16

MrJSmith
Advocate
Advocate
Don't hesitate to ask if you need any more help. What you are attempting to do is quite simple. The bigger issue is the method you want to carry out your goal. I.e. do you want this all in one command? Are there additional layers? Does it need to create / import the layers? Etc. If you define what you want it to do explicitly, it is a lot easier to write something to meet your needs.
0 Likes