EDIT ATTRIBUTE VALUE WITH LISP

EDIT ATTRIBUTE VALUE WITH LISP

Kyle.para
Advocate Advocate
14,860 Views
17 Replies
Message 1 of 18

EDIT ATTRIBUTE VALUE WITH LISP

Kyle.para
Advocate
Advocate

I am trying to get the below code to work and I am not having any luck.

 

I think it could be an issue with wcmatch because I am using #.

 

I need to be able to change the values of the tags for all the blocks that are in a particular drawing.

So I tried to enter " * " for all blocks.

 

Then I have tag named "ITEM %" (This name can change, but this is the name in this example) and I use "ITEM\u+0020%" if that matters.

 

The old value is # and the new value will be %.

 

(defun changeAttribValue (ent atttag oldval newval / entl)
  (while (and ent (/= "SEQEND" (cdr (assoc 0 (setq entl (entget ent))))))
    (and (= atttag (cdr (assoc 2 entl)))
         (= oldval (cdr (assoc 1 entl))) ;<- could use WCMATCH instead
         (entmod (subst (cons 1 newval) (assoc 1 entl) entl))
         (entupd ent)
         (mapcar 'princ (list "\n" oldval " -> " newval))
    )
    (setq ent (entnext ent))
  )
)

(defun C:CHATTRIB2 (/ ss a attag bname oldval newval)
  (and (/= "" (setq bname (getstring "\nBlock name: ")))
       (/= "" (setq tag (getstring T "\nFind Tag: ")))
       (/= "" (setq value (getstring T "\n:With Value: ")))
       (/= "" (setq attag (getstring T "\nTag: ")))
       (/= "" (setq oldval (getstring T "\nOld value: ")))
       (/= "" (setq newval (getstring T "\nNew value: ")))
       (setq a  0
             ss (ssget "X" (list '(0 . "INSERT") '(66 . 1) (cons 2 bname)))
       )
       (repeat (sslength ss)
         (changeAttribValue (ssname ss a) attag oldval newval)
         (setq a (1+ a))
       )
  )
)

 

Thanks,

0 Likes
Accepted solutions (1)
14,861 Views
17 Replies
Replies (17)
Message 2 of 18

john.uhden
Mentor
Mentor

Hi, Kyle.

 

My first reaction is that this would be better off using a dialog box where the oldval is provided and the user has a text_box for entering a newval, instead of all that typing (which fosters human error).

 

Regarding wcmatch, remember that certain characters are wildcards, like *, #, and ?, so if you want to match one of those characters literally, you have to reverse quote it like `#.

 

For example, (wcmatch "TAG#" "*#") would return nil because the last character is not a number,

  but

(wcmatch "TAG#" "*`#") would return T because the preceding reverse quote makes the following character literal.

 

I don't think that % is a wildcard character.

John F. Uhden

0 Likes
Message 3 of 18

Kyle.para
Advocate
Advocate

Hi John,

 

Yes I have something like that already, because I ran into this problem with the # in defining the tag.

 

 

(defun c:attr_rename_list ( / elst enm i new nmeLst old ss)

  (setq nmeLst ; All tags must be upper case.
    (list
      ;     OLD                                     NEW
      (list "#" "ITEM\U+0020%") ; \U+00A0 = Unicode non-breaking space.
      (list "I"                                  "#")
    )
  )

  (if (setq ss (ssget "_X" '((0 . "INSERT") (66 . 1))))
    (repeat (setq i (sslength ss))
      (setq enm (ssname ss (setq i (1- i))))
      (while
        (and
          (setq enm (entnext enm))
          (setq elst (entget enm))
          (= "ATTRIB" (cdr (assoc 0 elst)))
        )
        (if (setq new (cadr (assoc (setq old (cdr (assoc 2 elst))) nmeLst)))
          (progn
            (entmod (subst (cons 2 new) (cons 2 old) elst))
            (princ (strcat "\n" old " --> " new " "))
          )
        )
      )
    )
  )
  (princ)
)

That's the code I have.  The one problem I have with that code is that when it changes the tag name it's not permanent and when I attsync it goes back to normal, because it's not actually changing the tag in the block.  What I would like the prog to do is to change the tag name, then ask to change the prompt and then finally ask to change the value permanently.

 

It would be preferable to enter the values into the code so that it's all automated as I think you're suggesting. 

 

Kind of like the last code you made for me.

0 Likes
Message 4 of 18

hencoop
Advisor
Advisor

I use this routine for changing attribute values in multiple blocks of the same name:

It requires a lisp statement to use it.  E.g. (attupdm "<block name>" "<attribute tag>" "<new value>")

 

(defun attupdm (blockname attribname newvalue / ttl1ss iniblk inient nxtent)
  (setq ttl_exst nil
	bstp 0)
  (if
    (setq ttl1ss (ssget	;"x"
			(list (cons -4 "<and")
			  (cons 0 "INSERT")
			  (cons 2 blockname)
			  (cons -4 "and>")
			 )
		 ) ;_ end of ssget
    ) ;_ end of setq
    (while (< bstp (sslength ttl1ss))
     (progn
       (setq iniblk (ssname ttl1ss bstp))
       (setq inient (entget iniblk))
       (setq nxtent (entget (entnext iniblk)))
       (while (/= (cdr (assoc 0 nxtent)) "SEQEND")
	 (if (eq (cdr (assoc 0 nxtent)) "ATTRIB")
	   (if (eq (strcase(cdr (assoc 2 nxtent)))(strcase attribname))
	     (progn
	       (setq nxtent
		      (subst (cons 1 newvalue) (assoc 1 nxtent) nxtent)
	       ) ;_ end of setq
	       (entmod nxtent)
	       (entupd iniblk)
	     ) ;_ end of progn
	     (if (eq (strcase(cdr (assoc 2 nxtent)))"SHT_NO.")
	       (setq cur_sht_no (cdr(assoc 1 nxtent)))
	     )
	   ) ;_ end of if
	 ) ;_ end of if
	 (setq nxtent (entget (entnext (cdar nxtent))))
       ) ;_ end of while
       (princ)
     ) ;_ end of progn
      (setq bstp (1+ bstp))
    )
     (princ)
  ) ;_ end of if
  (princ)
) ;_ end of defun
AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 5 of 18

john.uhden
Mentor
Mentor
Somewhere in my wad of overcooked spaghetti called a brain I think I had
found that (entmod) won't work on changing attribute/attdef properties, but
(vlax-put object 'property newvalue) will. Try that.

John F. Uhden

0 Likes
Message 6 of 18

hencoop
Advisor
Advisor

ENTMOD works but you have to drill to the ATTRIB entity in each INSERT instance and use it on that entity.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 7 of 18

john.uhden
Mentor
Mentor

Thank you for that information, @hencoop.  "Ignorance may be bliss, but it also imposes limitations," John Uhden c. 2017.

 

Is your name really Henry Cooper?  Are you a relative of Rex Cooper?  How about Dr. Sheldon Cooper?

 

"coopdetat" is very clever.  I like it.

John F. Uhden

0 Likes
Message 8 of 18

Kyle.para
Advocate
Advocate

Thanks @hencoop.

 

I tried your code, but I can't get it to work.

I need to select all of the blocks on the drawing and change all of the values.

 

This is what tried. (attupdm "<*>" "<ITEM $>" "<$>") It asked me to select all of the blocks which I did but it then nothing happens.

I also tried it by just using the name, but it doesn't work.

 

@john.uhden

 

Another good friend helped me with implementing the tag change in the block.

He did like I think you were thinking. (setq doc (vla-get-activedocument (vlax-get-acad-object)))

 

Thanks fellas.

 

0 Likes
Message 9 of 18

hencoop
Advisor
Advisor

You do need to use the name of the block but you do not need to use the <> that brackets it.  Sorry for the confusion.

Try it this way (attupdm "name" "tag" "value") replacing each of the three strings with your own string.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 10 of 18

Kyle.para
Advocate
Advocate

Thanks coop that works now, but I am looking to change the value in the attribute within the block.

 

Probably just need to add something small to this code.

 

(defun c:AttRenList ( / doc new tagLst)

  (setq tagLst ; All tags must be upper case.
    (list
      ;     OLD                                     NEW
      (list "ITEM #"  "ITEM\U+0020$") ; \U+00A0 = Unicode non-breaking space.
      (list "#"       "ITEM\U+0020#")
    )
  )

  (setq doc (vla-get-activedocument (vlax-get-acad-object)))
  (vla-endundomark doc)
  (vla-startundomark doc)
  (vlax-for blk (vla-get-blocks doc)
    (if (= :vlax-false (vla-get-isxref blk))
      (vlax-for obj blk
        (cond
          ((= "AcDbBlockReference" (vla-get-objectname obj))
            (if (= :vlax-true (vla-get-hasattributes obj))
              (foreach att (vlax-invoke obj 'getattributes)
                (if (setq new (cadr (assoc (vla-get-tagstring att) tagLst)))
                  (vla-put-tagstring att new)
                )
              )
            )

          )
          ((= "AcDbAttributeDefinition" (vla-get-objectname obj))
            (if (setq new (cadr (assoc (vla-get-tagstring obj) tagLst)))
              (vla-put-tagstring obj new)
            )
          )
        )
      )
    )
  )
  (vla-endundomark doc)
  (princ)
)
0 Likes
Message 11 of 18

hencoop
Advisor
Advisor

Try this

(defun c:AttRenList ( / doc new tagLst)

  (setq tagLst ; All tags must be upper case.
    (list
      ;     OLD                                     NEW
      (list "ITEM #"  "ITEM\U+0020$") ; \U+00A0 = Unicode non-breaking space.
      (list "#"       "ITEM\U+0020#")
    )
  )

  (setq doc (vla-get-activedocument (vlax-get-acad-object)))
  (vla-endundomark doc)
  (vla-startundomark doc)
  (vlax-for blk (vla-get-blocks doc)
    (if (= :vlax-false (vla-get-isxref blk))
      (vlax-for obj blk
        (cond
          ((= "AcDbBlockReference" (vla-get-objectname obj))
            (if (= :vlax-true (vla-get-hasattributes obj))
              (foreach att (vlax-invoke obj 'getattributes)
                (if (setq new (assoc (vla-get-tagstring att) tagLst))
                  (progn
                    (vla-put-textstring att (cadr new))
                  )
                )
              )
            )

          )
          ((= "AcDbAttributeDefinition" (vla-get-objectname obj))
            (if (setq new (assoc (vla-get-tagstring obj) tagLst))
              (progn
                (vla-put-textstring obj (cadr new))
              )
            )
          )
        )
      )
    )
  )
  (vla-endundomark doc)
  (princ)
)
AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 12 of 18

Kyle.para
Advocate
Advocate

Coop sorry maybe I wasn't clear enough before, the code I had works fine for modifying the tag name withing the block.

 

What I would also like it to do is to have it modify the tag value at the same time.

 

For instance I have:

 

list "ITEM #"  "ITEM\U+0020$") ; \U+00A0 = Unicode non-breaking space.
      (list "#"       "ITEM\U+0020#")

 

Basically after I change the "tag name" to "ITEM $" etc... I would also like it to have another section for the "tag prompt", so I can change it to for example "Enter Item $" and then another section to change the "tag value" to say for example "$".

0 Likes
Message 13 of 18

hencoop
Advisor
Advisor

Do you want to set a static new attribute value or do you need to input each one?

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 14 of 18

Kyle.para
Advocate
Advocate

Static would be better, that way I can do it from the code and group everything that I need to change all into the same drawing.

0 Likes
Message 15 of 18

hencoop
Advisor
Advisor
Accepted solution

This will change the tag, prompt, and the value (I used tag values I have in a block to test it... replace with your own please.  Note that the tag must not have any spaces.):

 

EDIT: Sorry, I should have removed  (vlax-dump-object ...)  from the code.  I used that to debug my edits.  It is removed by this edit.

 

(defun c:AttRenList ( / doc new tagLst)

  (setq tagLst ; All tags must be upper case.
    (list
      ;     OLD TAG       NEW TAG      PROMPT      VALUE
      (list "SEC_DET_REF" "ITEM_#" "PROMPTSTRING1" "VALUE1") ; \U+00A0 = Unicode non-breaking space.
      (list "SHT_CALL_ON" "ITEM_$" "PROMPTSTRING2" "VALUE2")
    )
  )
  (setq doc (vla-get-activedocument (vlax-get-acad-object)))
  (vla-endundomark doc)
  (vla-startundomark doc)
  (vlax-for blk (vla-get-blocks doc)
    (if (= :vlax-false (vla-get-isxref blk))
      (vlax-for obj blk
        (cond
          ((= "AcDbBlockReference" (vla-get-objectname obj))
            (if (= :vlax-true (vla-get-hasattributes obj))
              (foreach att (vlax-invoke obj 'getattributes)
                (if (setq new (assoc (vla-get-tagstring att) tagLst))
                  (progn
                    (vla-put-tagstring att (cadr new))
                    (vla-put-textstring att (cadddr new))
                  )
                )
              )
            )

          )
          ((= "AcDbAttributeDefinition" (vla-get-objectname obj))
            (if (setq new (assoc (vla-get-tagstring obj) tagLst))
              (progn
                (vla-put-tagstring obj (cadr new))
                (vla-put-textstring obj (cadddr new))
                (vla-put-promptstring obj (caddr new))
              )
            )
          )
        )
      )
    )
  )
  (vla-endundomark doc)
  (princ)
)

 

 

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
Message 16 of 18

Kyle.para
Advocate
Advocate

This is close except one problem, it's not changing the attribute tag inside the block.

 

The code I posted on post 10 can do this, but it's only the tag name that changes and I can't change the prompt and value on that one.

 

Thanks

0 Likes
Message 17 of 18

Kyle.para
Advocate
Advocate

Hi @hencoop well I cannot explain it.

 

It works now.  Very strange.  I appreciate your help.

 

BTW where in the world is the edit post button? 

0 Likes
Message 18 of 18

john.uhden
Mentor
Mentor

Look at the "Options" pulldown on the upper right portion of your response (after posting).  If you get there in time (I think about 5 minutes) you can edit your response.  I've actually done it once.

John F. Uhden

0 Likes