LISP to remove only certain attributes from any block in a drawing

LISP to remove only certain attributes from any block in a drawing

Anonymous
Not applicable
7,159 Views
78 Replies
Message 1 of 79

LISP to remove only certain attributes from any block in a drawing

Anonymous
Not applicable

I'm in a unique situation and I've been trying to either create or find a LISP routine solution to my problem.

 

I have a large quantity of dynamic blocks that I extract coordinate information from.  Think a rectangles with 4 corners of data.  I use a predefined attribute called "COORDS_1", "COORDS_2", "COORDS_3", "COORDS_4", etc.... all the way up to "COORDS_12".  The attributes are fields tied to a "point / node" in the block that return coordinate information as an attribute value.

 

Since the attributes are predefined - when I redefine the blocks with new attribute data (each block has up to 50 attributes that are updated) - the predefined attributes try to maintain the old "preset" data instead of accepting the new data.  I can't use "constant" because the coordinate data won't update with the location in the drawing and only stays constant - referencing the location inside the block.

 

I've currently written an update lisp that compiles a list of drawing files from a folder, compares the drawing names in the folder with the block names ina  drawing.  Anything that matches gets redefined and attsync'd to the new values.  For the "Coords_#" attributes that are predefined - I would need the lisp routine to first remove any instance of "COORDS_#s" that matches the file names in the folder.

 

Any pointers or help to point me in the right direction would be great.  Thanks!

0 Likes
7,160 Views
78 Replies
Replies (78)
Message 2 of 79

SeeMSixty7
Advisor
Advisor

The benefit of attributes is they are attached at the insert level. Make any changes you do to the attributes independent of the blocks definition. So Attributes can be deleted.

 

@john.uhden came up with a pretty good utility for doing just that. See this post.

 

An alternative would be to just create a re-insert routine to do just what you wanted. Or have it just go through your attributed blocks and revise the data to match new block definition.

 

Good luck.

0 Likes
Message 3 of 79

Anonymous
Not applicable

Thanks - long time CAD user (r10) and very familiar with the inner workings of the attributes.  The only way I can get my automated "redefine" of the block to work is to completely remove all of the preset attributes prior to redefining with the new block that includes the coordinates.

 

The issue arises from the coordinate attributes being fields pointed to an object.  On the redefine - all preset attributes keep the old value (which points to an object that now no longer exist) so it's errors out and I get ##########

 

 

I'll check out the other routine you pointed me towards.  Thanks for your help!

0 Likes
Message 4 of 79

SeeMSixty7
Advisor
Advisor

R10! That's great. Me too!

 

Since you want to keep some attributes, you might want to consider just renaming the attribute tag of the one you want to remove with the redefine to something like DELETEME, then when you redefine the block (attsync or batman) the DELETELE attributes will be removed by the redefine.

 

Good luck,

 

 

Message 5 of 79

Anonymous
Not applicable

I'm basically doing the same already (I completely remove the attributes on the initial redefine) but then have to run a redefine a second time for the new value.  I was hoping I could, in LISP form - rename or delete all attributes in all blocks that start with "COORDS_" and then only one redefine.

 

Currently, one of our drawings that I have to run the routine on - takes nearly 40 hours of processing time to run twice (down from 60 with new computer upgrades) redefining up to 671 fully dynamic blocks with each pass.  If I could cut that time in 1/2 again but only having to redefine once - it would be huge!

 

Great to meet another r10 guys that remembers the glory days of upgrading to r12 and the plot dialog box!!!  haha   Let us all forget R13....lol

Message 6 of 79

SeeMSixty7
Advisor
Advisor

R12 was the bomb!!! I love R11 though because they introduced ADS and C Programming to the API's.

 

R13 was horrible!!!! Yes Let's try  and forget that ever happened. LOL

 

Wow that's a lot of processing. Sounds like there is some considerable room for improvement in the process. How many inserts are we talking about?

 

 

0 Likes
Message 7 of 79

Anonymous
Not applicable

A typical "model" would have near 27,000 dynamic blocks (they take around 15 hours x 2) and the largest model would have probably close to 60,000 dynamic blocks being redefined...

 

And yes...we burn through resources for sure!

 

CPU_MAXED.jpg

0 Likes
Message 8 of 79

Anonymous
Not applicable

Also, John's solution works great - except, there are several blocks where I need to keep my predefined attributes and can't remove all attributes at once.  Not to mention - there's not a solution for running ATTSYNC on the blocks when all attributes have been removed.  Thanks for the suggestions!

0 Likes
Message 9 of 79

john.uhden
Mentor
Mentor

ATTDEL.lsp could easily be enhanced to be selective about what attributes to delete, either by tag or value; wildcards welcome.  Tell me what you want to keep, or vice versa.

John F. Uhden

Message 10 of 79

Anonymous
Not applicable

I would love to delete any attributes that start with "COORDS_" in the list of blocks to be updated (I can work your code hopefully into my update routine to do the same - I currently build that list based on a directory call and then match it to a list (foreach I believe) in my drawing).

0 Likes
Message 11 of 79

john.uhden
Mentor
Mentor

Here ya go...

 

;; ATTDEL.LSP for d2cad by John F. Uhden 01-17-17
;; Just select inserts.  Those without attributes will be filtered out.
;; Actually, you can pick anything, but only inserts with attributes
;;   will be selected.
;; Attributes on locked layers will report errors, but
;;   the program will continue.
;; It does not change any block definition, but you could copy the
;;   emasculated block insertions.
;; v2.0 (06-20-17) revised to take a wcmatch value for tag names.
(defun C:ATTDEL ( / *error* err vars ss i obj atts m n Match)
  (vl-load-com)
  (defun *error* (err)
    (mapcar '(lambda (x)(setvar (car x)(cdr x))) vars)
    (vla-endundomark *doc*)
    (cond
      ((not err))
      ((wcmatch (strcase err) "*CANCEL*,*QUIT*"))
      (1  (princ (strcat "\nERROR: " err)))
    )
    (princ)
  )
  (or *acad* (setq *acad* (vlax-get-acad-object)))
  (or *doc* (setq *doc* (vla-get-ActiveDocument *acad*)))
  (vla-endundomark *doc*)
  (vla-startundomark *doc*)
  (setq vars (mapcar '(lambda (x)(cons x (getvar x))) '("cmdecho")))
  (mapcar '(lambda (x)(setvar (car x) 0))  vars)
  (command "_.expert" (getvar "expert")) ;; dummy command
  (and
    (setq n 0 m 0)
    (setq ss (ssget '((0 . "INSERT")(66 . 1))))
    (setq i (sslength ss))
    (setq Match (getstring "\nTagString to delete, e.g. 'COORDS_*': "))
    (setq Match (strcase Match))
    (while (> i 0)
      (setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
      (setq atts (vla-getattributes obj))
      (setq atts (vlax-variant-value atts))
      (foreach att (vlax-safearray->list atts)
        (setq m (1+ m))
        (if (and (wcmatch (strcase (vlax-get att 'TagString)) Match) (vl-catch-all-error-p (setq err (vl-catch-all-apply 'vla-delete (list att)))))
          (princ (strcat "\nERROR: " (vl-catch-all-error-message err)))
          (setq n (1+ n))
        )
      )
    )
  )
  (princ (strcat "\nDeleted " (itoa n) "/" (itoa m) " attributes."))
  (*error* nil)
)

John F. Uhden

Message 12 of 79

Anonymous
Not applicable

Thank you so much - it will be tomorrow before I have the chance to try it out.  If I wanted to integrate your code into my current lisp routine - do you think it would be much trouble to point the selection towards the same selection I've already created???  Haven't had a chance to review your code yet.

0 Likes
Message 13 of 79

Anonymous
Not applicable

I stayed after work a few minutes to try - I was too excited to leave!  lol

 

When I ran the routine - it deleted the "COORDS_*" attributes, however, they came back immediately when ATTSYNC'd.

 

I ran my updated routine after running your ATTDEL.lsp and IT WORKED!!!!!

 

You've saved my day!!!!!!!!!

 

MAJOR KUDOS to you.   Now, I just need to work it into my selection set so the designer doesn't pick a block that's not being updated.

 

0 Likes
Message 14 of 79

john.uhden
Mentor
Mentor

Yes, that can easily be done.  If you have trouble then any one of us can help you out.

John F. Uhden

Message 15 of 79

Anonymous
Not applicable

Here's the part of my LIPS routine where I'm trying to insert your code to work.   I compile a list of drawings in a directory (driven from a DCL selection drop down) and if those "FILES" match "BLKNAME" - it runs a simple redefine and attsync of that block.  I'm sure there's a fancier way of doing it but I'm still an old dog trying to learn new tricks!  lol  The VLA stuff is way over my head but working my way through.  I'm not having any luck so far but I'm keeping at it.

 

Thanks for everyone's help!

 

 

(progn						;start update
			(SETQ BLK_PATH (STRCAT BLK_PATH "/" MODEL "/"))
				(SETQ FILES
				(vl-directory-files BLK_PATH "*.DWG" 1)
			); END SETQ FILES			; COMPILES LIST "FILES" OF *.DWG FILES IN MODEL DIRECTORY
			(FOREACH FILENAME FILES			; STARTS REDEFINE PROCESS
				(SETQ BLKNAME (vl-filename-base filename)
				); END SETQ
			(IF (TBLSEARCH "BLOCK" BLKNAME)
				;IF STATEMENT IS TRUE
				(PROGN
					(command "-insert" (STRCAT BLKNAME "=" blk_path filename) #\ESCAPE
					); end insert
					(command "attsync" "N" blkname
					); end attsync
					(princ)
				); END PROGN
				;IF STATEMENT IS FALSE
				(princ)
			); END IF
			(princ)
			); END FOREACH
		(princ)
		); END prong
0 Likes
Message 16 of 79

john.uhden
Mentor
Mentor

You said that attsynch brings back the attributes, so where do we place my line code?  I will alter it to a non-command function that takes two arguments, the selection set and the tag specification, as in (attdel ss "COORDS_*").

 

In fact, here it is.  Place the function definition outside of your running code, but include the call to it wherever it works for you.

 

(defun ATTDEL (ss Match / i obj atts err m n)
  (vl-load-com)
  (and
    (= (type ss) 'PICKSET)
    (= (type Match) 'STR)
    (setq n 0 m 0)
    (setq i (sslength ss))
    (while (> i 0)
      (setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
      (setq atts (vla-getattributes obj))
      (setq atts (vlax-variant-value atts))
      (foreach att (vlax-safearray->list atts)
        (setq m (1+ m))
        (if (and (wcmatch (strcase (vlax-get att 'TagString)) Match) (vl-catch-all-error-p (setq err (vl-catch-all-apply 'vla-delete (list att)))))
          (princ (strcat "\nERROR: " (vl-catch-all-error-message err)))
          (setq n (1+ n))
        )
      )
    )
  )
  (princ (strcat "\nDeleted " (itoa n) "/" (itoa m) " attributes."))
  (princ)
)

John F. Uhden

Message 17 of 79

Anonymous
Not applicable

If you run the attdel.lsp on it's own - as you have it above - the attributes will return if you attsync the blocks afterwards.  However, it doesn't create an issue if you redefine the block immediately after the attdel - prior to the attsync.

 

I'm assuming your lisp removes the attributes from those "insertions" but not from the block itself.  When you then redefine the blocks - the "insertions" come back but they don't error.

 

Still working on getting it all integrated and pulled together but having fun trying to get it working.  Still struggling to get the command to work inside of the lisp...

 

I pasted it into the original "update" lisp routine and then used the following as part of my if statement but it's not working how I thought it would...still trying to work though the details to figure it out.  I've not got to do this for a while - it's been lots of fun.

 

(FOREACH FILENAME FILES			; STARTS REDEFINE PROCESS
				(SETQ BLKNAME (vl-filename-base filename)
				); END SETQ
			(IF (TBLSEARCH "BLOCK" BLKNAME)
				;IF STATEMENT IS TRUE
				(PROGN
					(attdel blkname "COORDS_*")
					(command "-insert" (STRCAT BLKNAME "=" blk_path filename) #\ESCAPE
					); end insert
					(command "attsync" "N" blkname
					); end attsync
					(princ)
				); END PROGN
				;IF STATEMENT IS FALSE
				(princ)
			); END IF
0 Likes
Message 18 of 79

john.uhden
Mentor
Mentor

No, it's not 

(attdel blkname "COORDS_*")

The first argument is supposed to be a selection set, not a string.  But if you would like it that way, we can modify it. 

John F. Uhden

Message 19 of 79

Anonymous
Not applicable

To help me learn - I'm using "Blkname" as a single selection of each block that meets the criteria of being in the drawing and in the redefine folder.  So I guess I would need to actually build a list of all blocks that meet that criteria into a selection set and then call that function?

 

May be easier to make it call to a string (blkname in my case) until I can wrap my head around the other.

 

You've been a tremendous amount of help!!!

0 Likes
Message 20 of 79

john.uhden
Mentor
Mentor

Maybe this will fix all that...

 

(defun ATTDEL (ss Match / err ss i obj atts m n)
(vl-load-com)
(and
(= (type ss) 'PICKSET)
(= (type Match) 'STR)
(setq n 0 m 0)
(setq i (sslength ss))
(while (> i 0)
(setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
(setq atts (vla-getattributes obj))
(setq atts (vlax-variant-value atts))
(foreach att (vlax-safearray->list atts)
(setq m (1+ m))
(if (and (wcmatch (strcase (vlax-get att 'TagString)) Match) (vl-catch-all-error-p (setq err (vl-catch-all-apply 'vla-delete (list att)))))
(princ (strcat "\nERROR: " (vl-catch-all-error-message err)))
(setq n (1+ n))
)
)
)
) ;; end AND
(princ (strcat "\nDeleted " (itoa n) "/" (itoa m) " attributes."))
(> n 0) ; to return T if attributes were deleted, so that the (and ...) below continues
) ;; end DEFUN

(and (setq ss (ssget "X" (list '(0 . "INSERT")(cons 2 BLKNAME)'(66 . 1)))) ;; only inserts with attributes (attdel ss "COORDS_*") (vl-cmdf "-insert" (STRCAT BLKNAME "=" blk_path filename) #\ESCAPE) (vl-cmdf "attsync" "N" blkname) ); END AND

John F. Uhden