Author Topic: Finding nested blocks from a list of blocks  (Read 5881 times)

0 Members and 1 Guest are viewing this topic.

Rabbit

  • Guest
Finding nested blocks from a list of blocks
« on: October 23, 2012, 07:20:22 PM »
What I'm trying to do is go through all of the blocks in a drawing and finding the ones with nested blocks of equipment tags.  Once I find these, I'm using an Excel file to get the proper information associated with equipment tag, and then putting that into a table.

I have code for getting the excel info, and code to put the info into a table.

What I'm struggling with is getting the list of blocks, and dynamic blocks, which have tags in it.

I've gotten really really far: (sarcasm)

(setq Allblocks (ssget "_X" '((0 . "INSERT"))))
  (foreach a Allblocks  [clueless here]


I've searched and found several examples to try to reverse engineer and extract out what will work for me.  Obviously, I didn't get very far. Lee Mac's FindNestedBlock.lsp routine actually gave a return, but it was the equipment tag's name.  Not what I was expecting.

I just need a list of all of the blocks which have this "EPQMTAG" block in it.

Any help?
Rabbit

JasonB

  • Newt
  • Posts: 38
  • perfectionist trapped inside the mind of an idiot.
Re: Finding nested blocks from a list of blocks
« Reply #1 on: October 23, 2012, 08:51:21 PM »
When you say nested blocks, you are referring to a block within a block? You're not meaning attributes attached to a block?

You also mention Dynamic blocks. In effect these are a type of nested block. As far a I'm aware you can't just look at the block name for these. As soon as you change them from their default state, they are replaced by an anonymous block *U*. You need to look at their effective name instead. I would assume with these you would have to gather their effective name + their current parameter states to be of any use. For example you may have a dynamic block of a Door, which has parameters for: jamb size, door size, left/right hang. You would need to collect all this information along with the effective block name to correctly identify which version of the door has been used.

I have a bit of code that creates a selection set base on a given block name. Maybe it gets you a bit further in your quest.

Code - Auto/Visual Lisp: [Select]
  1. ; Function to create a selection set of standard & dynamic blocks
  2. ; That have the same Effective name
  3. (defun BlockSSET (blname / sset ss-dyn num ent effname)
  4.         (vl-load-com) ; ensure that ActiveX is loaded.
  5.         (if (not (setq sset (ssget "X" (list (cons 2 blname))))) ; Select block with standard method. This will also capture dynamic blocks in their default state.    
  6.                 (setq sset (ssadd)) ; if nothing is found then create and empty selection set
  7.         )
  8.         (if (setq ss-dyn (ssget "X" (list (cons 2 "`*U*")))) ; select all anonymous blocks. this will also capture any dynamic blocks that have been worked with
  9.                 (progn ; If anonymous blocks found, check for the given effective name
  10.                         (setq num 0) ; zero counter
  11.                         (repeat (sslength ss-dyn) ; repeat for the number of objects in the selection set
  12.                                 (setq ent (ssname ss-dyn num)) ; find the entity name
  13.                                 (setq effname (vla-get-EffectiveName (vlax-ename->vla-object ent))) ; Find the EffectiveName of the block
  14.                                 (if (= blname effname) ; if the blockname matches the effective name
  15.                                         (ssadd ent sset) ; then add it to the original selection set
  16.                                 ); end if
  17.                         (setq num (1+ num)) ; Increment the counter by one
  18.                         )
  19.                 )
  20.         )
  21. ; return the gathered selection set
  22. sset
  23. )

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Finding nested blocks from a list of blocks
« Reply #2 on: October 24, 2012, 06:17:05 AM »
I just need a list of all of the blocks which have this "EPQMTAG" block in it.

Consider the following function:

Code - Auto/Visual Lisp: [Select]
  1. (defun _listofblockswithnestedblock ( nested / lst name )
  2.         (if
  3.             (and
  4.                 (= :vlax-false (vla-get-islayout block))
  5.                 (= :vlax-false (vla-get-isxref block))
  6.                 (not (wcmatch (setq name (vla-get-name block)) "`**"))
  7.             )
  8.             (vlax-for obj block
  9.                 (if
  10.                     (and
  11.                         (= "AcDbBlockReference" (vla-get-objectname obj))
  12.                         (or
  13.                             (and
  14.                                 (vlax-property-available-p obj 'effectivename)
  15.                                 (= (strcase nested) (strcase (vla-get-effectivename obj)))
  16.                             )
  17.                             (= (strcase nested) (strcase (vla-get-name obj)))
  18.                         )
  19.                         (not (member name lst))
  20.                     )
  21.                     (setq lst (cons name lst))
  22.                 )
  23.             )
  24.         )
  25.     )
  26.     lst
  27. )

To be called with the name of your nested block:
Code - Auto/Visual Lisp: [Select]
  1. (_listofblockswithnestedblock "EPQMTAG")

The above will iterate over the block definitions found in the Block Collection of the active drawing and will iterate over the objects found within each definition. If the definition contains a block reference with the specified name, the parent block is added to the list of names returned. Primary references of the supplied block are ignored (since Layout Definitions are not processed), XRefs are ignored, and anonymous blocks are ignored (though, you may wish to include these, depending on how you are using the resultant list).

Note that this only tests for a first level of block nesting, i.e. if Block A is nested within Block B, and Block B is nested within Block C, calling the function to search for Block A will only return Block B, and not Block C.

For any level of nesting, consider my Nested Block Counter program.
« Last Edit: October 24, 2012, 06:21:40 AM by Lee Mac »

Rabbit

  • Guest
Re: Finding nested blocks from a list of blocks
« Reply #3 on: October 24, 2012, 08:06:32 PM »
Thanks for the input guys.

BricscadBoy:  I couldn't get yours to work properly for me.  It would find the tags that were not nested.  All I could find were dynamic blocks.  I think it's just returning the name of the parent block.  But, I did get some valuable insight on the workings of it.

Lee:  I was able to adapt your code, but it only gives me the parent blocks name.  What I'm needing is the names of only the nested blocks.  I couldn't figure out how to get it.  It would find all the blocks with tags.  One of my adaptations is for it to add to the list the EQMPTAG blocks that are all alone, not nested.

Here's what I came up with:

Code: [Select]
(defun _listofblockswithnestedblock ( nested / lst name )
  (vlax-for block (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)));for each block in drawing do the code
    (if;if block is in layout, is xref and if is anonymous, skip to next block
      (and
(= :vlax-false (vla-get-islayout block))
        (= :vlax-false (vla-get-isxref block))
        (not (wcmatch (setq name (vla-get-name block)) "`**"))
      )
      (vlax-for obj block;for all other blocks, do code
(if
  (and
    (= "AcDbBlockReference" (vla-get-objectname obj))
            (or
      (and
(vlax-property-available-p obj 'effectivename)
                (= (strcase nested) (strcase (vla-get-effectivename obj)))
              );and
      (= (strcase nested) (strcase (vla-get-name obj)))
            );or
    (not (member name lst))
          );and
          (setq lst (cons name lst))
        );if
      );vlax-for
    );if
  );vlax-for
  lst
);defun

(defun LM:ss->ent ( ss / i l )
    (if ss (repeat (setq i (sslength ss)) (setq l (cons (cdr (assoc 2 (entget (ssname ss (setq i (1- i)))))) l))))
);defun

;;;USE
(setq Allblocks (_listofblockswithnestedblock "EQPMTAG"))
  (setq Allblocks (append (LM:ss->ent (ssget "_X" '((2 . "EQPMTAG")))) Allblocks))

I've attached a 2012 Autocad file showing the blocks I'm working with.  What I'm needing is a list of the equipment tag blocks only.  Or even better, a list with the entity names like when you do (car (entsel)). With that I'll be able to do everything else I need, which includes getting a count of those tags and then getting info from and Excel file that coordinates with the tags number.

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Re: Finding nested blocks from a list of blocks
« Reply #4 on: October 24, 2012, 10:32:40 PM »
Replace this
(setq lst (cons name lst))
with this
(setq lst (cons (vlax-vla-object->ename obj) lst))

[Untested]  :-D
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

Rabbit

  • Guest
Re: Finding nested blocks from a list of blocks
« Reply #5 on: October 25, 2012, 10:02:12 AM »
That's what I needed CAB.  After some experimenting this is what I came up with:

Code: [Select]
;;;------------------------------------------------------------------------------------------------
;;;Thanks to J. Roper for this section:
;;---------------------------------------------------------
;ATT:GETS - Utility routine to get all attributes for
;an insert object (EN).
;;
;;You pass this function an entity name, like something that would be returned from this example:
;;(setq EN (car (entsel "\nSelect block")))
;;an entity name looks like this when returned on a command line  --><Entity name: 4119ab90>
;;You use this function like this:  (setq ATTLIST (ATT:GETS EN))
;;it returns a list like this ---> (("TAG1 . "VALUE1")("TAG2" . "VALUE2")....)
(defun ATT:GETS (EN / EL RES)
  ;entnext goes on to the next entity.  If what you passed is an attributed block, the next entity will be an attribute.
  (setq EN (entnext EN)
        EL (entget EN));entget "breaks open" the entity name to reveal the entity list
  (while (= "ATTRIB" (cdr (assoc 0 EL)));we check the entity list to see if it is an attribute
    (setq RES (cons
                (cons (cdr (assoc 2 EL));if it is, we create a list of its tag and value, dxf code 2 and 1, using CONS
                      (cdr (assoc 1 EL))) RES)
          EN (entnext EN);go to the next entity
          EL (entget EN)));get the next entities list
  (reverse RES);reverse the list to put it in proper order.
  )

(defun _listofblockswithnestedblock ( nested / lst name )
  (vlax-for block (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)));for each block in drawing do the code
    (if;if block is in layout, is xref and if is anonymous, skip to next block
      (and
(= :vlax-false (vla-get-islayout block))
        (= :vlax-false (vla-get-isxref block))
        (not (wcmatch (setq name (vla-get-name block)) "`**"))
      )
      (vlax-for obj block;for all other blocks, do code
(if
  (and
    (= "AcDbBlockReference" (vla-get-objectname obj))
            (or
      (and
(vlax-property-available-p obj 'effectivename)
                (= (strcase nested) (strcase (vla-get-effectivename obj)))
              );and
      (= (strcase nested) (strcase (vla-get-name obj)))
            );or
    (not (member name lst))
          );and
;;;          (setq lst (cons name lst))
;;;   (setq lst (cons (vlax-vla-object->ename obj) lst))
;;;   (setq lst (cons (ATT:GETS (vlax-vla-object->ename obj)) lst))
;;;   (setq lst (cons (car (ATT:GETS (vlax-vla-object->ename obj))) lst))
  (setq lst (cons (cdar (ATT:GETS (vlax-vla-object->ename obj))) lst)) ;;;ATT:GETS is a sub-routine I use to get attribute info.

;;;(setq lst (cons  (cdar (ATT:GETS (car (entsel)))) lst))

 
        );if
      );vlax-for
    );if
  );vlax-for
  lst
);defun

(defun LM:ss->ent ( ss / i l )
;;;  (if ss (repeat (setq i (sslength ss)) (setq l (cons (cdr (assoc 2 (entget (ssname ss (setq i (1- i)))))) l))))
  (if ss (repeat (setq i (sslength ss)) (setq l (cons (cdar (ATT:GETS (ssname ss (setq i (1- i))))) l))))
);defun

;;;USE
(setq Allblocks (_listofblockswithnestedblock "EQPMTAG"))
  (setq Allblocks (append (LM:ss->ent (ssget "_X" '((2 . "EQPMTAG")))) Allblocks))

It will return a list of all the tags' numbers as strings in a list.  I know there's probably a more elegant way of doing this, but it works for me.

PS, I'll be sure to give credit in the code when I'm done.  Thanks for the help guys.  This has been a great learning experience.  I'm sure I'll be asking more later.

Rabbit