dfaunce Posted August 3, 2017 Posted August 3, 2017 So I'm trying to use Lee Mac's LISP function here: http://www.cadtutor.net/forum/showthread.php?84816-Find-and-Select-blocks-that-have-certain-attributes-via-command-line What I need to do is: I have a block with - Block Name = "VIFCD_001" - Attribute = "TAG1" Value = "-M161" ** The Value of Attribute TAG1 is always unique, however there could be multiple VIFCD_001 blocks, each with different TAG1 values** I need a LISP routine that finds the block Where TAG1= and then change the attribute "DESC" of that specific block to Lee's LISP function is able to select the block by tag name which is great, but I can't seem to figure out how to tell it to then look for the DESC attribute in that block and change the value. (defun c:setAttrVals (tagName descName) ..... ..... .... (princ) ) Can anyone help? Quote
BIGAL Posted August 4, 2017 Posted August 4, 2017 You would start by pick block and get its name a more universal approach, then make a selection of all blocks with that name, then go through all those blocks checking attribute Tagname=TAG1 then look at the value and change it. For a do all style approach I have been lately using the attribute position rather than a tag name required this works for any block name. Try this (defun c:test ( / oldtag1 obj bname newstr) (setq oldtag1 "TAG1") ; attribute tag name (setq obj (vlax-ename->vla-object (car (entsel "\nPick object")))) (setq bname (vla-get-name obj)) ; block name need a check for picked a block (setq ss (ssget "x" (list (cons 0 "INSERT") (cons 2 bname)))) (princ "\n") (setq newstr (getstring "Please enter new value")) (setq x (sslength ss)) (foreach att (vlax-invoke (vlax-ename->vla-object (ssname SS (setq x (- x 1)) )) 'getattributes) (if (= oldtag1 (strcase (vla-get-tagstring att))) (vla-put-textstring att newstr) ) ; end if ) ; foreach ) Quote
Lee Mac Posted August 5, 2017 Posted August 5, 2017 Consider the following code: (defun c:doit ( / bln des ent enx flg fnd idx new sel tag ) (setq bln "VIFCD_001" tag "DESC" ) (if (setq sel (ssget "_X" (list '(0 . "INSERT") '(66 . 1) (cons 2 bln)))) (progn (setq fnd (strcase (getstring t "\nSpecify attribute value to find: ")) new (cons 1 (getstring t (strcat "\nSpecify new value for \"" tag "\" attribute: "))) ) (repeat (setq idx (sslength sel)) (setq ent (entnext (ssname sel (setq idx (1- idx)))) enx (entget ent) des nil flg nil ) (while (= "ATTRIB" (cdr (assoc 0 enx))) (cond ( (= tag (strcase (cdr (assoc 2 enx)))) (setq des enx) ) ( (or flg (setq flg (wcmatch (strcase (cdr (assoc 1 enx))) fnd)))) ) (setq ent (entnext ent) enx (entget ent) ) ) (if (and des flg) (if (entmod (subst new (assoc 1 des) des)) (entupd (cdr (assoc -1 des))) ) ) ) ) (princ (strcat "\nNo blocks called \"" bln "\" found in the active drawing.")) ) (princ) ) Quote
Grrr Posted August 5, 2017 Posted August 5, 2017 Lee, why don't you use the 'flg' variable to replicate (vl-some) when iterating thru the attribs, using the (while) loop: (while (and (= "ATTRIB" (cdr (assoc 0 enx))) (not flg) ) ... ); while Quote
Lee Mac Posted August 5, 2017 Posted August 5, 2017 Lee, why don't you use the 'flg' variable to replicate (vl-some) when iterating thru the attribs, using the (while) loop: (while (and (= "ATTRIB" (cdr (assoc 0 enx))) (not flg) ) ... ); while What if the wcmatch expression is validated before the 'tag' attribute is encountered? Quote
Grrr Posted August 5, 2017 Posted August 5, 2017 What if the wcmatch expression is validated before the 'tag' attribute is encountered? Ahh I see now, didn't read carefully what the task was = misunderstood the coding. FWIW, heres another approach (copied the user-prompts from you) : (defun C:test ( / bnm tag n SS fnd new i o L ) (setq bnm "VIFCD_001" tag "DESC" ) (and (setq n (_AttdefPosition bnm tag)) (setq SS (ssget "_X" (list '(0 . "INSERT") '(66 . 1) (cons 2 (strcat "`**," bnm))))) (setq fnd (strcase (getstring t "\nSpecify attribute value to find: "))) (setq new (getstring t (strcat "\nSpecify new value for \"" tag "\" attribute: "))) (repeat (setq i (sslength SS)) (and (eq bnm (vla-get-EffectiveName (setq o (vlax-ename->vla-object (ssname SS (setq i (1- i))))))) (setq L (vlax-invoke o 'GetAttributes)) (vl-some (function (lambda (x) (wcmatch (strcase (vla-get-TextString x)) fnd))) L) (vla-put-TextString (nth n L) new) ) ) ) (princ) ); defun C:test (defun _AttdefPosition ( bnm tgnm / e i enx f ) (and (setq e (tblobjname "BLOCK" bnm)) (setq e (cdr (assoc -2 (entget e)))) (setq i -1) (while (and e (not f)) (setq enx (entget e)) (setq e (entnext e)) (and (member '(0 . "ATTDEF") enx) (setq i (1+ i))) (setq f (member (cons 2 tgnm) enx)) (and (member '(0 . "SEQEND") enx) (setq e nil)) ) ) (if f i) ); defun _AttdefPosition I saw you tried to limit up to only one iteration while processing the attributes, its obvious you like to think out such clever methods. Quote
Lee Mac Posted August 5, 2017 Posted August 5, 2017 FWIW, heres another approach:< ... > I would recommend not relying upon the order in which attribute definitions are encountered in the block definition corresponding to the order in which attribute references are encountered in each block reference, as it is possible to generate attribute references independently of the block definition, hence one cannot guarantee that the two will be entirely consistent. When working with attributes, it is almost always better to differentiate the attributes using their attribute tags (unless you are forced into an alternative, for example, if duplicate tags have been used - IMO, AutoCAD should prevent this from happening). I saw you tried to limit up to only one iteration while processing the attributes, its obvious you like to think out such clever methods. Thanks - though, it is always worth striking a balance between efficiency & readability: ruthlessly efficient code can become incredibly unreadable and therefore difficult to maintain in a general application. Quote
Lee Mac Posted August 5, 2017 Posted August 5, 2017 FWIW, if you wanted to use vl-some to limit the iteration, here's another approach: (defun c:doit ( / bln dsc flg fnd idx new sel tag ) (setq bln "VIFCD_001" tag "DESC" ) (if (setq sel (ssget "_X" (list '(0 . "INSERT") '(66 . 1) (cons 2 bln)))) (progn (setq fnd (strcase (getstring t "\nSpecify attribute value to find: ")) new (getstring t (strcat "\nSpecify new value for \"" tag "\" attribute: ")) ) (repeat (setq idx (sslength sel)) (if (vl-some '(lambda ( att ) (or flg (setq flg (wcmatch (strcase (vla-get-textstring att)) fnd))) (or dsc (if (= tag (strcase (vla-get-tagstring att))) (setq dsc att))) (and flg dsc) ) (vlax-invoke (vlax-ename->vla-object (ssname sel (setq idx (1- idx)))) 'getattributes) ) (vla-put-textstring dsc new) ) (setq flg nil dsc nil) ) ) (princ (strcat "\nNo blocks called \"" bln "\" found in the active drawing.")) ) (princ) ) (vl-load-com) (princ) Quote
Grrr Posted August 6, 2017 Posted August 6, 2017 I would recommend not relying upon the order in which attribute definitions are encountered in the block definition corresponding to the order in which attribute references are encountered in each block reference, as it is possible to generate attribute references independently of the block definition, hence one cannot guarantee that the two will be entirely consistent. Thanks Lee - I wanted to hear your oppinion about that (experiment) code I posted, as I don't have that much of experience, although I'd usually go straight with the TagString comparsion approach on the attribute references. When working with attributes, it is almost always better to differentiate the attributes using their attribute tags (unless you are forced into an alternative, for example, if duplicate tags have been used - IMO, AutoCAD should prevent this from happening). Ahh yes, I've forgot about that - before I was iterating over all attribute references, until I learned how to use (vl-some) and forgot about the possibility of duplicate tags. Now I understand why you decided to process all attribs per block. Thanks - though, it is always worth striking a balance between efficiency & readability: ruthlessly efficient code can become incredibly unreadable and therefore difficult to maintain in a general application. I agree on that, unless that code is used as a subfunction, so you just need to know what are gonna be your inputs in it and what should be returned. Then one can save the debugging headaches, by parsing inputs. i.e.: (setq elephant (cow->elephant (sheep->cow (dog->sheep (cat->dog (mouse->cat (fly->mouse fly))))))) I'm just leaving my oppinion to the one who reads this (not trying to tutor you ). FWIW, if you wanted to use vl-some to limit the iteration, here's another approach:<...> Thanks for posting your suggestion with (vl-some) - it shall boggle a few brains, that are trying to learn something new. I liked this lambda part you did: '(lambda ( att ) (or flg (setq flg (wcmatch (strcase (vla-get-textstring att)) fnd))) (or dsc (if (= tag (strcase (vla-get-tagstring att))) (setq dsc att))) (and flg dsc) ) It seems very handy method to limit the iteration if there are multiple requirements for different items. Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.