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?
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) )
@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).
@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.
@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....
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
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) )
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)