EIA Posted January 29 Posted January 29 (edited) Hi, sorry if the title is too global, I couldn't find a precise way to describe this. I am learning AutoLISP, and sometimes I test my luck by asking ChatGPT for the code; 99% of the time this just leads me to failure, but I once got a code that I got to work with some little mods: (defun changeAttribute (block attribute newValue / ss i en ed att) (setq ss (ssget "X" (list (cons 2 block)))) ; Select every block with the name stored in "block" (if ss ;If selection set exists: (progn (setq i 0) ;Starts "i" to be used as counter in while loop (while (< i (sslength ss)) ; Activate while loop until it reaches end of selection set (all blocks have been affected (setq en (ssname ss i)) ; Get block entity name (setq ed (entget en)) ; Get block entity data through its name (setq i (1+ i)) ; Add 1 to the counter (setq att (entnext en)) ; Get to the first attribute in the block (???) (while (and att (eq (cdr (assoc 0 (entget att))) "ATTRIB")) ;(???) (if (eq (cdr (assoc 2 (entget att))) attribute) ; If attribute name is the same as the string stored in "attribute" (???) (entmod (subst (cons 1 newValue) (assoc 1 (entget att)) (entget att))) ; Change its value with "newValue" (???) ) (setq att (entnext att)) ; Get to the next attribute ) ) (princ "\nAttributes updated.") ;This gets printed on command prompt after while loop ) (princ "\nNo blocks were found.") ;Second list on if statement so this gets printed if selection set fails ) (princ) ;Function doesn't return anything so it doesn't consume memory ) Basically I don't understand what's going on in the parts where I comment (???). Can anyone shed some light on this topic for me? I feel like this is way too important to just ignore, since blocks are a great way for non-lisp users to store values, and that can get really helpful in my working environment. Thanks! Edit: adding some clarifications: The function is supposed to search for every block with "x" name, then take every attribute with "y" value and change it to "z" value. It does its job wonderfully. What I don't understand: (setq att (entnext en)) ; Get to the first attribute in the block (???) From what I've seen in the Autodesk help page, entnext gets the entity in the drawing that has been placed after the entity I put there in "en"; how does this get an attribute? I thought the attributes would be a property of the entity that is the block, but apparently they are their own entity? (while (and att (eq (cdr (assoc 0 (entget att))) "ATTRIB")) ;(???) I may actually understand this one: first it sees if att contains anything, then checks if the property 0 of att is "ATTRIB", which would mean that this is an attribute. The thing that always confuses me here is how properties are managed, is there any way to see what that 0 represents? Because it is quite easy for this one, but I remember that getting the property of a text background color was a pain in the ass. (if (eq (cdr (assoc 2 (entget att))) attribute) ; If attribute name is the same as the string stored in "attribute" (???) We checked in the while loop if this was an attribute, now we check if what this attribute contains corresponds to what we are trying to change (assoc looks for an association list with a "2" in the att entity, cdr returns the second value which is the value that corresponds that 2, eq compares it to attribute, because that 2 stores the value of the attribute). Same problem than before, how can I know that the attribute is stored in that value? (entmod (subst (cons 1 newValue) (assoc 1 (entget att)) (entget att))) ; Change its value with "newValue" (???) entmod confuses my head in ways I have never experienced before, the example I get from Autodesk help page is this: I think I kind of understand it, at least the general concept, but that "cons" also gets me lost; I can barely explain what's happening here. Hope this helps, any info on any of this is really appreciated! Edited January 29 by EIA Quote
GLAVCVS Posted January 29 Posted January 29 Hi EIA. I'm sure there's someone who can explain it better than me. But I'll try: The code '0' is associated with the object type. Therefore, if '(cdr (assoc 0 en))' returns 'ATTRIB', it means that the object being analyzed is an attribute. In the database, blocks are always stored in this way: first the main container entity, then the entities that compose it up to an object type 'ENDBLK' that serves to end the main entity. Since objects in AutoCAD are stored in order, to access the objects that compose a block, after obtaining the entity name of the main container entity, you must use 'ENTNEXT' successively until the returned object is an 'ENDBLK' object. 1 Quote
GLAVCVS Posted January 29 Posted January 29 As for how to get the value contained in the attribute, once you have obtained the list of data from the entity 'ATTRIB', you should be able to get it with '(cdr (assoc 1 listEntityATTRIB))' Quote
EIA Posted January 29 Author Posted January 29 On 1/29/2025 at 9:17 AM, GLAVCVS said: Hi EIA. I'm sure there's someone who can explain it better than me. But I'll try: The code '0' is associated with the object type. Therefore, if '(cdr (assoc 0 en))' returns 'ATTRIB', it means that the object being analyzed is an attribute. In the database, blocks are always stored in this way: first the main container entity, then the entities that compose it up to an object type 'ENDBLK' that serves to end the main entity. Since objects in AutoCAD are stored in order, to access the objects that compose a block, after obtaining the entity name of the main container entity, you must use 'ENTNEXT' successively until the returned object is an 'ENDBLK' object. Expand So, the way I see it; AutoLISP gets a selection set (ssget), from that set you can get an object (ssname), and from that object you can get its entities (entget, actually I am now seeing that this part is not used at all in the code, thanks ChatGPT). But, when you get an object, a block in this case (with ssname), you don't really get the whole block, but only what you call the "container entity", which is only the first entity, so with entnext you can cycle through all its entities, eventually reaching to its attributes (those where its property 0 is "ATTRIB") and finally to the last entity (with a property 0 of "ENDBLK"). I made an attempt to draw the way I think all of this works: Is this any correct? I think I may be wrong in the fact that ssname gets directly the object type instead of a global intermediate thing that contains every element of the block. Also thanks a lot for the answer! Even though I still have many questions, that clarifies some things. Quote
GLAVCVS Posted January 29 Posted January 29 'ssname' gets the object (entity name) whose index is given as an argument for a given selection set. One important detail I must stress is that 'ssget' only does sets of 'parent' graphical objects. That is, it will never include the objects included in any of those 'parent' objects. Quote
BIGAL Posted January 31 Posted January 31 You may be better for this task to look at VL coding. yes similar approach, get a ssget, then look at the block, you can do a check "hasattributes" to make sure the block does have attributes if so then (setq atts (valx-get obj 'attributes)) this makes a list of the attributes, you can use (foreach att atts and get the attribute 'Tagname check against your tagname and if a match (vlax-put att 'textstring newvalue). Will try to find a simple example. (setq x (sslength ss)) (foreach att (vlax-invoke (vlax-ename->vla-object (ssname SS (setq x (1- x))) 'getattributes) (if (= tagname (strcase (vlax-get att 'tagstring))) (vla-put-textstring att newstr) ) ) ) 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.