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,158 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,159 Views
78 Replies
Replies (78)
Message 21 of 79

Anonymous
Not applicable

Thank you so much for your work!  Looks like I just need to copy and paste that over my redefine parts of my LISP routine since you've included the redefine and attsync.  I've got a meeting this evening I have to leave for but will be doing this first thing tomorrow!

 

Major kudo's for all of the help!!!!

0 Likes
Message 22 of 79

Anonymous
Not applicable

John,  thanks again for your help - been hitting my head all morning.  Here's the working Code part of my LISP and where I am.  When I run it - I get "** Error: bad argument type:  fixnump: nil **" and the routine stops.

 

If I block the the (SETQ ss (ssget.....) and the following (attdel ss "COORDS_*") lines - it runs the redefine for each block and subsequent attsync.  However, it doesn't remove the "COORDS_*" attributes.

 

Obviously - I'm in over my head a bit here getting this figured out.  I'm assuming the (setq ss (ssget "X" (list '(0 . "INSERT")(cons 2 BLKNAME)'(66 . 1)))) line is making a selection set "SS" of any inserted blocks with attributes that are found in the "BLKNAME" string.  However, I think in my code - BLKNAME isn't a selection set and just a string and that's the issue.

 

Any help you can give to get me past this hurdle is very much appreciated again!

 

Thank you.

 

Here's the code as I have it now:

 

(defun ATTDEL (ss Match / err ss i obj atts m n)
 (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
(princ)
			(command "undo" "C" "N")
		(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
					(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)
						(princ)
				); END PROGN
				;IF STATEMENT IS FALSE
				(princ)
			); END IF
			(princ)
			); END FOREACH
		(princ)
		); END progn
			(command "regenall")
			(command "undo" "A")
			(alert (strcat  DWG_NAME " has been updated to the " MODEL " model."))
			(PROMPT (strcat  DWG_NAME " has been updated to the " MODEL " model."))
			(PRINC); FINISH CLEANLY
	);progn
	);end if userclick
(princ)
0 Likes
Message 23 of 79

john.uhden
Mentor
Mentor

I really dislike looking at other people's code.  BeeKee and Kent and Ranjit are so good at it that I am jealous.  One thing I saw is that I don't think blk_path is set anywhere, but that would not give you a fixnump error.

John F. Uhden

0 Likes
Message 24 of 79

Anonymous
Not applicable

blk_path is set from a dialog box selection - the code I posted was just a snip-it of the entire routine.  From all that I can tell from messing with it yesterday - I think the modified ATTDEL is looking for a selection set and all I'm returning is a single instance of a block name.

 

I'll keep trying to work at it and figure it out.  I wrote the original 5 years ago now so it's been a while since I've dug into LISP.

 

Thanks again for the major help you've given!

0 Likes
Message 25 of 79

Anonymous
Not applicable

Looking over every thing again - how hard would it be to modify this to look for a string and not a selection set?  I've tried changing "'PICKFIRST" to "'STR" and that didn't work.

 

(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)
)
0 Likes
Message 26 of 79

john.uhden
Mentor
Mentor

This one takes the block name instead of a selection set.

It then creates the selection set, so there's no need to do that elsewhere, except that it is local.

If you want to use the same SS later then remove SS from the list of locals.

 

(defun ATTDEL (BlkName Match / err SS i obj atts m n)
  (vl-load-com)
  (and
    (setq n 0 m 0)
(= (type BlkName) 'STR) (= (type Match) 'STR) (setq ss (ssget "X" (list '(0 . "INSERT")(cons 2 BlkName)'(66 . 1)))) (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.")) (> n 0) ;; to return T if any attributes were deleted. )

John F. Uhden

Message 27 of 79

Anonymous
Not applicable

John,

 

Thanks again for all of the help you've given.  I have the latest version working without any routine errors inside of my LISP routine.

 

However, I call for it using (attdel Blkname "COORDS_*") and the command window returns: "Deleted 0/0 attributes."

 

I've verified that the blocks have the "COORDS_*" attributes - and the routine continues to execute and redefines the blocks but with the error from the preset fields that we are trying to eliminate.

 

If I use the stand-alone LISP routine - it finds and executes it perfectly well.  Thanks again for your help!

0 Likes
Message 28 of 79

john.uhden
Mentor
Mentor

Something is awry.  Are you sure you fed it the name of a block with attributes?

Maybe you had already run attdel and you ran it again, wherein you would get 0/0.

Maybe you fed it "coords_*" in lower case.  It has to be upper case.  I hadn't mentioned that.

Maybe I have really lost it.

 

I did find a mistake.  If you use an incorrect attribute tag it will report m/m deleted, but not really.  It just means that it didn't find any matching attributes.  That has to do with the way I wrote the error catch.  I should fix that.

John F. Uhden

Message 29 of 79

john.uhden
Mentor
Mentor

Okay.  I think this one is bullet-proof...

 

(defun ATTDEL (BlkName Match / err ss i obj atts m n)
  (vl-load-com)
  (and
    (or
      (= (type BlkName) 'STR)
      (prompt "\nBlock name argument is not a string.")
    )
    (or
      (tblsearch "Block" BlkName)
      (prompt (strcat "\nBlock named \"" BlkName "\" is not defined."))
    )
    (or
      (= (type Match) 'STR)
      (prompt "\nAttribute tag match argument is not a string.")
    )
    (setq Match (strcase Match))
    (or
      (setq ss (ssget "X" (list '(0 . "INSERT")(cons 2 BlkName)'(66 . 1))))
      (prompt (strcat "\nNo insertions of \"" BlkName "\" with attributes found."))
    )
    (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 (wcmatch (strcase (vlax-get att 'TagString)) Match)
          (if (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."))
  (> n 0) ;; to return T if any attributes were deleted.
)

The rest is up to you.

John F. Uhden

Message 30 of 79

Anonymous
Not applicable

John,

 

Thanks again for your help.  I will try my best to sort through all of the issues and see what I can find.

 

Currently, using your latest code - I get the error "No Insertions of "DG_HIGHLY CONSUMABLES_BEVERAGES_WATER" with attributes found."  However, I have verified that the block is inserted, and the "COORDS_1, COORDS_2, COORDS_3, COORDS_4, etc..." are still in the block.  Hopefully, I'll be able to sort through and figure out the issue....  Thanks again for so much help!

 

 

Also, a few observations for those that may find this thread in the future:

  • I'm using all dynamic blocks.
    • The ATTDEL routine will not remove the attributes from the parent block definition for the entire drawing- just the per insertion of the block
    • Meaning, if you attsync the existing block - the attributes that you removed - will come right back as they are still in the parent definition.
    • Assuming, this to be from the dynamic block use - I also discovered it wasn't really "redefining" the block to the new update as well.
      • If I change all of the entities to the color red and used the "vl-cmdf "-insert" command calls - the block never redefine nor changed colors back to the new block definition.
        • Not really certain on why but if I went back to the older style (command "-insert"...) code - the block always redefined.
        • The same applied to the "Attsync" command

Once I'm able to get it all sorted through - I'll post the code that made it work and any other observations I see along the way.  Again, thanks for all of your help, John!!!

0 Likes
Message 31 of 79

john.uhden
Mentor
Mentor
Might there supposed to be an underscore between "HIGHLY CONSUMABLES?"

Or maybe they are really "COMBUSTIBLES?"

Or maybe there is no ending "S?" That would make sense for an adverb to
modify an adjective, not an adverb modifying a plural noun.

John F. Uhden

0 Likes
Message 32 of 79

Anonymous
Not applicable

I've attached a few screen grabs confirming the block name, inclusion of the multiple "COORDS_1", etc.... attributes, as well as a manual run-through of the command process with the same results.  For some reason - it's not seeing the block insertion in the drawing.  Might be from the difference between a dynamic block vs a non-dynamic.  I'm in the process of creating with a non-dynamic block to see if it works in that environment.

 

Thanks again for your effort, code, and your above and beyond help!

 

0 Likes
Message 33 of 79

Anonymous
Not applicable

Just ran a test using non-dynamic block with the COORDS_1, _2, _3, _4 attributes.... No error on running the attdel command inside of the LISP routine... It removed the COORDS_* attributes and worked fine.  However, the parent block still showed the "COORDS_*" attributes in the block editor but ATTSYNC will not bring them back.  I have to reinsert the block to get the COORDS_* back.

 

I think this is really coming down to a dynamic block vs non-dynamic and how they react differently.  That said - the ATTDEL will only work for dynamic blocks when intending to redefine the block to bring back the preset attributes.

0 Likes
Message 34 of 79

john.uhden
Mentor
Mentor

I'm still running only 2002.  Might a dynamic block insert be other than an "INSERT?"  Like maybe a "DINSERT" or something?

Do the attributes actually belong to a different block name?

Might the attributes actually be text or mtext?

Might the consumables be found if they were BEER instead of WATER?  (reminiscent of the Cone Heads)

 

Sure wish I had a newer release to test things on my own, but I'm lousy at picking winning lottery numbers.

John F. Uhden

0 Likes
Message 35 of 79

Anonymous
Not applicable

Dynamic blocks insert just like any other block and function for the most part - like a standard non-dynamic block.

 

There are a few exceptions - for instance, you can't open a dynamic block outside of the block editor without potentially causing an issue.  However, I know in the data world of AutoCAD - that every time you click a grip, move, stretch, etc... it creates a brand new entityID number and can quickly bloat your drawing.  This makes linking data to them more difficult than in the past.

 

I believe - that's what we are seeing in the code.  What I can't figure out - is the difference in your stand alone routine that works great - and the in place function that doesn't.

 

Without asking you to spend much more time - is there a modifier I can use to call your original ATTDEL that you have in this thread in a LISP (C:ATTDEL) and then apply a selection select instead of manually getting the objects through the pickbox?

 

My thought is - I could build a list of blocknames to be redefine, make that list a selection set, and then send that as a selection to use the ATTDEL on???

 

Again - you've been able to get me closer than any other person and I appreciate the feedback and help!

0 Likes
Message 36 of 79

john.uhden
Mentor
Mentor

C:ATTDEL and ATTDEL are basically identical except that c:ATTDEL gets all block insertions and asks for the attribute tag match.  In your situation C:ATTDEL is not as useful as ATTDEL.

 

I am wondering if it would be helpful to have ATTDEL delete the attribute definitions from the block definition as well, but that has nothing to do with getting the prompt that "No insertions ... found."

 

However "Deleted 0/0 attributes" makes sense if the selection set of insertions was empty ("No insertions ... found.").

 

The only things that make sense to me are either:

1.  The blkname was misspelled so that no insertions were found, or

2.  The attributes were already deleted.

3.  I HAVE really lost it.

 

Earlier in the function it checks for the existence of the blkname definition and it would prompt if no definition was found, so maybe there really are no insertions of blkname.  Have you tried other blknames?

 

Here's a test to run.  It will enable grips on all insertions of whatever blkname you enter...

 

(defun c:TEST ( / name)
  (setvar "gripblock" 1)
  (if (setq name (getstring T "\nEnter block name to find: "))
    (if (setq ss (ssget "X" (list '(0 . "INSERT")(cons 2 name)'(66 . 1))))
       (sssetfirst ss)
       (prompt "\nNo insertions found by that name.")
    )
  )
  (princ)
)

John F. Uhden

Message 37 of 79

Anonymous
Not applicable

Ran your test routine... it worked for non-dynamic blocks.... I even took a block and edited it to make it dynamic - and once it went dynamic - it wasn't found.  So I think we've for sure narrowed down to dynamic vs. non-dynamic block differences.

 

I've attached two screen captures of my text blocks with "ATTDISP" set to "ON" so all attributes are visible.  The before is with all attributes - the after, is after running the "C:ATTDEL" routine and manually selecting the blocks using a crossing window.  It works perfectly.

 

I'm assuming - with the differences in the dynamic vs non-dynamic blocks - the selection set isn't working based on how the dynamic blocks are handled in the "data" of the drawing.

0 Likes
Message 38 of 79

Anonymous
Not applicable

Don't have time right now but I think the answer is found in this snippet from here:

 

https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/vla-get-effectivename-found-but-ssge...

 

 

To get dynamic blocks, search for all block references. Then loop 
 through the active selection set looking for the blocks you want by using 
 the effectivename property. Something like (not tested)
 {code}
 (if (ssget '(0 . "insert"))
 (vlax-for n 
 (vla-get-activeselectionset(vla-get-activedocument(vlax-get-acad-object)))
 (if (wcmatch (vla-get-effectivename n) pattern)
 (progn ....))))
 {code}
0 Likes
Message 39 of 79

john.uhden
Mentor
Mentor

Just for kicks, copy and paste the following into the AutoCAD command prompt, select any portion of a dynamic block, and copy and post what it returns, not via a screen shot that I can't read.  I want to look for any special (or different) DXF codes.  The DXF codes are critical to selection set filtering.

 

(entget (car (entsel)))

John F. Uhden

Message 40 of 79

Anonymous
Not applicable

Sure thing - anything to help!

 

Selected the "DG_HIGHLY CONSUMABLES_BEVERAGES_WATER" block that I've been using...

 

here's what it returned:

 

((-1 . <Entity name: 1a33f286b50>) (0 . "INSERT") (5 . "8D8E3925") (102 . "{ACAD_XDICTIONARY") (360 . <Entity name: 1a33f286b60>) (102 . "}") (330 . <Entity name: 1a35bf8c820>) (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "DG_MERCH_POGS") (100 . "AcDbBlockReference") (66 . 1) (2 . "*U43") (10 8.0 7.875 0.0) (41 . 1.0) (42 . 1.0) (43 . 1.0) (50 . 0.0) (70 . 0) (71 . 0) (44 . 0.0) (45 . 0.0) (210 0.0 0.0 1.0))

0 Likes