Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Select blocks by attribute layer

9 REPLIES 9
Reply
Message 1 of 10
bill.smoker
1263 Views, 9 Replies

Select blocks by attribute layer

I would like to select blocks based on the layer of one or more attributes.  The obvious method would be to isolate that layer and select the ones with attributes that display. But many of the values are empty! Any suggestions?

9 REPLIES 9
Message 2 of 10
Lee_Mac
in reply to: bill.smoker

Try the following:

 

(defun c:selblkbyattlay ( / att atx ent idx lay sel tag )
    (if
        (and (/= "" (setq tag (getstring "\nSpecify attribute tag: ")))
            (progn
                (while
                    (not
                        (or (= "" (setq lay (getstring t "\nSpecify attribute layer:")))
                            (tblsearch "layer" lay)
                        )
                    )
                    (princ (strcat "\nLayer " lay " doesn't exist."))
                )
                (/= "" (setq lay (strcase lay)))
            )
        )
        (if
            (and
                (setq sel
                    (ssget "_X"
                        (list
                           '(0 . "INSERT")
                           '(66 . 1)
                            (if (= 1 (getvar 'cvport))
                                (cons 410 (getvar 'ctab))
                               '(410 . "Model")
                            )
                        )
                    )
                )
                (progn
                    (repeat (setq idx (sslength sel))
                        (setq ent (ssname sel (setq idx (1- idx)))
                              att (entnext ent)
                              atx (entget  att)
                        )
                        (while
                            (and
                                (= "ATTRIB" (cdr (assoc 0 atx)))
                                (/= lay (strcase (cdr (assoc 8 atx))))
                            )
                            (setq att (entnext att)
                                  atx (entget  att)
                            )
                        )
                        (if (= "SEQEND" (cdr (assoc 0 atx)))
                            (ssdel ent sel)
                        )
                    )
                    (< 0 (sslength sel))
                )
            )
            (sssetfirst nil sel)
            (princ
                (strcat
                    "\nNo blocks found with \""
                    tag
                    "\" attribute on layer \""
                    lay
                    "\"."
                )
            )
        )
    )
    (princ)
)
Message 3 of 10
Kent1Cooper
in reply to: bill.smoker


@bill.smoker wrote:

I would like to select blocks based on the layer of one or more attributes.  The obvious method would be to isolate that layer and select the ones with attributes that display. But many of the values are empty! Any suggestions?


Lee's suggestion asks for a Tag, which doesn't sound to me like what you're looking for, if "one or more attributes" may be on the Layer you're checking.  And it appears to look at all the pieces in every insertion of all Blocks with Attributes, which doesn't seem necessary if there may be more than one insertion of any given Block definition -- if the Attribute definition on the desired Layer is in one, it will be in all of that Block name.

 

Here's a routine that doesn't look at block insertions, but rather at Block definitions, and makes a list of the names of all those that contain any Attributes on the desired Layer, regardless of how many there are or what their Tags are, then highlights all insertions of all such Blocks.  Am I interpreting what you're after correctly?  Minimally tested.

 

(defun C:TEST (/ layname blknamestr blkname blkdefdata ent edata foundone)
  (while
    (not
      (and
        (setq layname (strcase (getstring "\nLayer of Attribute definition(s) to look for: ")))
        (tblsearch "layer" layname)
      ); and
    ); not
    (prompt "\nNo Layer by that name in drawing.")
  ); while
  (setq blknamestr ""); initially empty
  (while (setq blkname (cdadr (tblnext "block" (not blkname))))
    (setq blkdefdata (entget (tblobjname "block" blkname)))
    (if (member '(70 . 2) blkdefdata); has non-constant Attributes and is not Xref*
      (progn ; then
        (setq ent (cdr (assoc -2 (entget (tblobjname "block" blkname)))))
          ; first entity in Block definition
        (while (and ent (not foundone))
          ; look until it runs out of entities or finds at least one Attribute on that Layer
          (setq edata (entget ent))
          (if
            (and
              (= (cdr (assoc 0 edata)) "ATTDEF"); it's an Attribute definition, and
              (= (cdr (assoc 8 edata)) layname); it's on the desired Layer
            ); and
            (setq ; then
              blknamestr (strcat blknamestr blkname ","); put into comma-delimited string
              foundone T ; to stop (while) loop
            ); setq
          ); if
          (setq ent (entnext ent)); move on [nil when it gets to end]
        ); while
      ); progn
    ); if
    (setq foundone nil); for next Block definition
  ); while
  (sssetfirst nil (ssget "_X" (list (cons 2 blknamestr)))); highlight all insertions of Blocks so identified
); defun

 

 

* EDIT:  I have it looking for Blocks with non-constant Attributes, because of the possibility that some that you're looking for may be empty, which presumably you wouldn't do with constant-value Attributes.  The DXF-code-70 information about Block definitions says about the value of 2:

 

2 = This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all)

 

So I wasn't sure a Block with both constant and non-constant Attributes would be "seen" by that criterion.  So I made a Block with both a constant Attribute and a non-constant one, and its (tblobjname) data still has (70 . 2) in it, and the routine found it looking for the Layer of either of the Attributes  So I think that should say "...is not set if the block has only attribute definitions that are constant...."

 

Values of 4 and up are all about Xrefs in one way or another, and a value of 1 is about anonymous Blocks that I don't think can have Attributes, so those get bypassed when looking only for (70 . 2).

Kent Cooper, AIA
Message 4 of 10
Lee_Mac
in reply to: Kent1Cooper


@Kent1Cooper wrote:

And it appears to look at all the pieces in every insertion of all Blocks with Attributes, which doesn't seem necessary if there may be more than one insertion of any given Block definition -- if the Attribute definition on the desired Layer is in one, it will be in all of that Block name.


And what if the attribute reference layer has been changed for a given block reference? - Your program will produce incorrect results.

Message 5 of 10
Kent1Cooper
in reply to: Lee_Mac


@Lee_Mac wrote:
....

And what if the attribute reference layer has been changed for a given block reference? - Your program will produce incorrect results.


I confess, it would never have occurred to me that anyone would want to do that, or maybe even that one could do that, but I very rarely use Blocks with Attributes.  So if that's a possibility that needs to be accounted for....

Kent Cooper, AIA
Message 6 of 10
bill.smoker
in reply to: bill.smoker

Thank you for your responses. As Lee Mac suggests, many of the attributes have had the layer changed for an individual block reference. It was these that I need to select. So Kent's solution, which looks for layers in the block definition, doesn't quite do what I am looking for. It does however have some useful code which I might utilise for something else later!

 

So Lee's version fits the bill very nicely. I don't need to specify the tags, so I might remove that element of his code. But it is easily circumvented by using an asterisk wild card for the tag name.

 

Thanks again for all of your help, this was really frustrating me yesterday afternoon!

 

Regards,

Bill Smoker

Message 7 of 10
Lee_Mac
in reply to: bill.smoker

You're very welcome Bill, happy to help!

 

Looking back over my code, I'm not entirely sure why I included the prompt for attribute tags, as I don't actually test the value of the attribute tag string later on in the code! I must've changed my mind about the program halfway through writing it...

 

Nevertheless, here is an updated version with the redundant prompt removed:

 

(defun c:selbyattlay ( / att atx ent idx lay sel )
    (while
        (not
            (or (= "" (setq lay (getstring t "\nSpecify attribute layer:")))
                (tblsearch "layer" lay)
            )
        )
        (princ (strcat "\nLayer " lay " doesn't exist."))
    )
    (if (/= "" (setq lay (strcase lay)))
        (if
            (and
                (setq sel
                    (ssget "_X"
                        (list
                           '(0 . "INSERT")
                           '(66 . 1)
                            (if (= 1 (getvar 'cvport))
                                (cons 410 (getvar 'ctab))
                               '(410 . "Model")
                            )
                        )
                    )
                )
                (progn
                    (repeat (setq idx (sslength sel))
                        (setq ent (ssname sel (setq idx (1- idx)))
                              att (entnext ent)
                              atx (entget  att)
                        )
                        (while
                            (and
                                (= "ATTRIB" (cdr (assoc 0 atx)))
                                (/= lay (strcase (cdr (assoc 8 atx))))
                            )
                            (setq att (entnext att)
                                  atx (entget  att)
                            )
                        )
                        (if (= "SEQEND" (cdr (assoc 0 atx)))
                            (ssdel ent sel)
                        )
                    )
                    (< 0 (sslength sel))
                )
            )
            (sssetfirst nil sel)
            (princ (strcat "\nNo blocks found with attributes on layer \"" lay "\"."))
        )
    )
    (princ)
)
Message 8 of 10
pbejse
in reply to: bill.smoker

Another, to account for "FUDGE" and "CONSTANT"

 

(defun c:Test2 (/ LayerAtt n sel cnt e layerd layerdSS)
  (defun LayerAtt (f en ln)
    (vl-some (function (lambda (a)
			 (if (eq ln (vla-get-layer a))
			   en
			 )
		       )
	     )
	     (vlax-invoke (vlax-ename->vla-object en) f)
    )
  )
  (if (and (setq n (getstring T "\nSpecify attribute layer: "))
	   (/= n "")
	   (setq n (cdr (assoc 2 (tblsearch "layer" n))))
	   (setq layerdSS (ssadd)
		 sel	  (ssget "_X"
				 (list
				   '(0 . "INSERT")
				   '(66 . 1)
				   (cons 410 (getvar 'ctab))
				 )
			  )
	   )
	   (repeat (setq cnt (sslength sel))
	     (setq e (ssname sel (setq cnt (1- cnt))))
	     (If
	       (or	(layeratt "GetAttributes" e n)
				(layeratt "GetConstantAttributes" e n)
			    )
		(ssadd e layerdSS) e
	     )
	   )
      )
    (sssetfirst nil layerdSS)
  )
  (princ
    (cond
      ((null n) "\n<<Invalid Layer Name>>")
      ((null sel)
       "\n<<No Attribute Blocks Found on current layout>>"
      )
      ((zerop (sslength layerdSS))(strcat "\nNo blocks found with attributes on layer \"" n "\""))
      ( T "")
    )
  )
  (princ)
)(vl-load-com)

 

Message 9 of 10
Lee_Mac
in reply to: pbejse

Message 10 of 10
pbejse
in reply to: Lee_Mac


@Lee_Mac wrote:

@pBe: FUDGE?


I meant this LM:

 

"many of the attributes have had the layer changed for an individual block reference"

 

Fudging in Autocad - an intent of temporary solution that inadvertently  stays permanent. <- my definition 

 

Smiley Very Happy

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost