Updating existing dynamic blocks from csv data

Updating existing dynamic blocks from csv data

Gorra
Advocate Advocate
709 Views
15 Replies
Message 1 of 16

Updating existing dynamic blocks from csv data

Gorra
Advocate
Advocate

Hello,

I am trying to build a LISP that

-selects all dynamic blocks of a given name (done)

-extracts the attributes for the blocks (done, maybe?)

-removes blocks from the selection set with a blank value for a certain attribute (stuck)

-loads csv data into a list of lists (done)

-matches the remaining blocks to corresponding rows in the csv data (maybe done if I could get the LISP that far)

-updates the attribute data in the blocks (probably done if I could get that far)

 

I think I have the base code written for all of it, I'm error checking now and hitting a wall filtering the blocks with nil data

 

The current subfunction is here. Much of it is from @ronjonp, heavily modified from a previous project.

 

  (defun SelectBlocks ( / name n out)
      (if (setq name "SPLICE CLOSURE"  ;;selecting all dynamic blocks of this name
      Blklst (ssget "_X" '((0 . "INSERT")))
      n -1
      out (ssadd)
      )
    (while (setq blck (ssname Blklst (setq n (1+ n))))
        (if (= :vlax-true (vla-get-IsDynamicBlock (vlax-ename->vla-object blck)))
          (if (= (strcase (vla-get-Effectivename (vlax-ename->vla-object blck))) (strcase name))
            (ssadd blck out)
          )
        )
    )
    )
  (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
  (if (/= 0 (sslength out))(sssetfirst nil out)(princ (strcat "No Dynamic Block found by the Name - " name))) ;;if no blocks error
  (foreach blck BlkLst ;;for each block 
    (progn
      (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
      (setq d (vlax-invoke (vlax-ename->vla-object blck) 'getattributes)) ;;Get attributes
      (setq AttLst (cons (append (mapcar 'vla-get-textstring d)) AttLst)) ;;Compile list of attribute values
      (alert "got attributes")

      (setq ExZ 0.0)

      (cons ExZ AttLst)
      (setq ExEst (getpropertyvalue blk "Position/Y"))
      (cons ExEst Attlst)
      (setq ExNrth (getpropertyvalue blk "Position/X"))
      (cons ExNrth AttLst)
      (if (= (nth 3 AttLst) nil) ;;if SPLICE_NUMBER attribute is blank then
        (vl-remove blck Blklst) ;;remove block from selection set
      )
(alert "removed blanks")
    )
  )
)

 

I get a 'bad argument type: consp <Selection set: be>' error at about the 'for each' line. 

 

The full code is attached below

 

Thanks for any insights. 

0 Likes
Accepted solutions (1)
710 Views
15 Replies
Replies (15)
Message 2 of 16

paullimapa
Mentor
Mentor

Give this a try...not tested but should return a selection set of matching dynamic blocks that contain values in the attributes...

 

; SelectBlocks finds matching dynamic blocks and removes those with empty attribute values
; OP: 
; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/updating-existing-dynamic-blocks-from-csv-data/m-p/12321664#M456318
(defun SelectBlocks ( / name n out dBlklst mewdBlklst newout)
      (if (setq name "SPLICE CLOSURE"  ;;selecting all dynamic blocks of this name
      Blklst (ssget "_X" '((0 . "INSERT")))
      n -1
      out (ssadd)
      )
    (while (setq blck (ssname Blklst (setq n (1+ n))))
        (if (= :vlax-true (vla-get-IsDynamicBlock (vlax-ename->vla-object blck)))
          (if (= (strcase (vla-get-Effectivename (vlax-ename->vla-object blck))) (strcase name))
            (ssadd blck out)
          )
        )
    )
    )
;  (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
;;  (if (/= 0 (sslength out))(sssetfirst nil out)(princ (strcat "No Dynamic Block found by the Name - " name))) ;;if no blocks error
 (if (/= 0 (sslength out))
  (progn
   (setq dBlklst (mapcar 'cadr (ssnamex out))) ;; convert filtered dynamic block only selection set to entity list don't need to vl-remove-if since already filtered that out with while loop above
   (setq newdBlklst dBlklst) ; make a copy of this entity list to use to remove block entities with empty attributes
   (sssetfirst nil out)  
   (foreach blck dBlklst ; BlkLst ;;for each block in filtered entity list
;      (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
      (setq d (vlax-invoke (vlax-ename->vla-object blck) 'getattributes)) ;;Get attributes
      (setq AttLst (cons (append (mapcar 'vla-get-textstring d)) AttLst)) ;;Compile list of attribute values
      (alert "got attributes")
      (setq ExZ 0.0)
      (cons ExZ AttLst)
      (setq ExEst (getpropertyvalue blk "Position/Y"))
      (cons ExEst Attlst)
      (setq ExNrth (getpropertyvalue blk "Position/X"))
      (cons ExNrth AttLst)
      (if (= (nth 3 AttLst) nil) ;;if SPLICE_NUMBER attribute is blank then
       (progn
;        (vl-remove blck Blklst) ;;remove block from selection set
        (setq newdBlklst (vl-remove blck newdBlklst)) ;;remove block entity with empty attribute value from new entity list
        (alert "removed blanks")
       )
      )
;      (alert "removed blanks")
   )  ; foreach
   (if(sslength newdBlklst) ; if any entities left in selection set
     (progn
      (setq newout (ssadd)) ; convert entitiy list back to selection set
      (foreach itm newdBlklst
       (ssadd itm newout)
      )
      (sssetfirst nil newout)  
     )
    (sssetfirst nil nil)  ; clear
   ) ; if
  ) ; progn
  (princ (strcat "No Dynamic Block found by the Name - " name)) ; else
 ) ; if
) ; defun SelectBlocks

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 3 of 16

Gorra
Advocate
Advocate

@paullimapa Thanks so much for the work, especially the comments. The script is getting past the "removed blanks" alert, then gives a 'bad argument type: lselsetp' and then gives a long list of entity names at the console.  This happens before the alert for the function completing. Is it a selset being encountered where it shouldn't be or is it looking for one that isn't there?

 

Gorra

0 Likes
Message 4 of 16

paullimapa
Mentor
Mentor

I just noticed the error of my ways.

Instead of using (sslength) which only works for selection sets:

(if(sslength newdBlklst)
I should use (length) which works with lists:
(if(length newdBlklst)

 

; SelectBlocks finds matching dynamic blocks and removes those with empty attribute values
; OP: 
; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/updating-existing-dynamic-blocks-from-csv-data/m-p/12321664#M456318
(defun SelectBlocks ( / name n out dBlklst mewdBlklst newout)
      (if (setq name "SPLICE CLOSURE"  ;;selecting all dynamic blocks of this name
      Blklst (ssget "_X" '((0 . "INSERT")))
      n -1
      out (ssadd)
      )
    (while (setq blck (ssname Blklst (setq n (1+ n))))
        (if (= :vlax-true (vla-get-IsDynamicBlock (vlax-ename->vla-object blck)))
          (if (= (strcase (vla-get-Effectivename (vlax-ename->vla-object blck))) (strcase name))
            (ssadd blck out)
          )
        )
    )
    )
;  (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
;;  (if (/= 0 (sslength out))(sssetfirst nil out)(princ (strcat "No Dynamic Block found by the Name - " name))) ;;if no blocks error
 (if (/= 0 (sslength out))
  (progn
   (setq dBlklst (mapcar 'cadr (ssnamex out))) ;; convert filtered dynamic block only selection set to entity list don't need to vl-remove-if since already filtered that out with while loop above
   (setq newdBlklst dBlklst) ; make a copy of this entity list to use to remove block entities with empty attributes
   (sssetfirst nil out)  
   (foreach blck dBlklst ; BlkLst ;;for each block in filtered entity list
;      (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
      (setq d (vlax-invoke (vlax-ename->vla-object blck) 'getattributes)) ;;Get attributes
      (setq AttLst (cons (append (mapcar 'vla-get-textstring d)) AttLst)) ;;Compile list of attribute values
      (alert "got attributes")
      (setq ExZ 0.0)
      (cons ExZ AttLst)
      (setq ExEst (getpropertyvalue blk "Position/Y"))
      (cons ExEst Attlst)
      (setq ExNrth (getpropertyvalue blk "Position/X"))
      (cons ExNrth AttLst)
      (if (= (nth 3 AttLst) nil) ;;if SPLICE_NUMBER attribute is blank then
       (progn
;        (vl-remove blck Blklst) ;;remove block from selection set
        (setq newdBlklst (vl-remove blck newdBlklst)) ;;remove block entity with empty attribute value from new entity list
        (alert "removed blanks")
       )
      )
;      (alert "removed blanks")
   )  ; foreach
   (if(length newdBlklst) ; if any entities left in selection set
     (progn
      (setq newout (ssadd)) ; convert entitiy list back to selection set
      (foreach itm newdBlklst
       (ssadd itm newout)
      )
      (sssetfirst nil newout)  
     )
    (sssetfirst nil nil)  
   ) ; if
  ) ; progn
  (princ (strcat "No Dynamic Block found by the Name - " name)) ; else
 ) ; if
) ; defun SelectBlocks

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 5 of 16

Gorra
Advocate
Advocate

@paullimapa  Thanks, that is closer to working. I think the code is right but I'm trying to reference the wrong object/list. The blocks with blank entries aren't being deselected. When I princ the value I'm using as the test (nth 3 AttLst), it spews all the attributes. When I set it to (nth 3 d), it spews all of the attributes  as vla-object data. If I set it to (nth 3 blck) I get a bad argument type. Have I come at this in the wrong direction? Or is this a case of selection set vs entity list command differences?

 

Thanks,

Gorra

0 Likes
Message 6 of 16

paullimapa
Mentor
Mentor

**updated**

So here's one way to check if any of the attributes collected from the block entity in question has any empty strings:

First I would include (setq attvallst... in this line of code:

 

(setq AttLst (cons (append (setq attvallst(mapcar 'vla-get-textstring d))) AttLst)) ;;Compile list of attribute values & place values into a list = attvallst

 

Then use a while loop to test if the (strlen) of any of the values are zero:

 

(setq n (length attvallst))
      (while (> n 0) ; do while loop to test if there are any empty attribute values
        (if(zerop(strlen (nth (setq n (1- n)) attvallst)))
          (setq n -1 attvallst nil) ; if any found will nullify attvallst and stop loop
        ) ; if  
      ) ; while

 

If any empty strings found will nullify attvallst & I'll remove that entity from the newBlklst list:

 

      (if (not attvallst) ; if no list exists then must have found at least one empty attribute value
       (progn
;        (vl-remove blck Blklst) ;;remove block from selection set
        (setq newdBlklst (vl-remove blck newdBlklst)) ;;remove block entity with empty attribute value from new entity list
        (alert "removed blanks")
       )
      )

 

 So give this modified code a try:

 

; SelectBlocks finds matching dynamic blocks and removes those with empty attribute values
; OP: 
; https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/updating-existing-dynamic-blocks-from-csv-data/m-p/12321664#M456318
(defun SelectBlocks ( / AttLst attvallst blck Blklst d dBlklst ExEst ExNrth ExZ name n newdBlklst newout out) ; localize variables
      (if (setq name "SPLICE CLOSURE"  ;;selecting all dynamic blocks of this name
      Blklst (ssget "_X" '((0 . "INSERT")))
      n -1
      out (ssadd)
      )
    (while (setq blck (ssname Blklst (setq n (1+ n))))
        (if (= :vlax-true (vla-get-IsDynamicBlock (vlax-ename->vla-object blck)))
          (if (= (strcase (vla-get-Effectivename (vlax-ename->vla-object blck))) (strcase name))
            (ssadd blck out)
          )
        )
    )
    )
;  (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
;;  (if (/= 0 (sslength out))(sssetfirst nil out)(princ (strcat "No Dynamic Block found by the Name - " name))) ;;if no blocks error
 (if (/= 0 (sslength out))
  (progn
   (setq dBlklst (mapcar 'cadr (ssnamex out))) ;; convert filtered dynamic block only selection set to entity list don't need to vl-remove-if since already filtered that out with while loop above
   (setq newdBlklst dBlklst) ; make a copy of this entity list to use to remove block entities with empty attributes
   (sssetfirst nil out)  
   (foreach blck dBlklst ; BlkLst ;;for each block in filtered entity list
;      (vl-remove-if 'listp (mapcar 'cadr (ssnamex Blklst))) ;;remove if not a list?
      (setq d (vlax-invoke (vlax-ename->vla-object blck) 'getattributes)) ;;Get attributes
;      (setq AttLst (cons (append (mapcar 'vla-get-textstring d)) AttLst)) ;;Compile list of attribute values
      (setq AttLst (cons (append (setq attvallst(mapcar 'vla-get-textstring d))) AttLst)) ;;Compile list of attribute values & place values into a list = attvallst
      (alert "got attributes")
      (setq ExZ 0.0)
      (cons ExZ AttLst)
      (setq ExEst (getpropertyvalue blk "Position/Y"))
      (cons ExEst Attlst)
      (setq ExNrth (getpropertyvalue blk "Position/X"))
      (cons ExNrth AttLst)
      (setq n (length attvallst))
      (while (> n 0) ; do while loop to test if there are any empty attribute values
        (if(zerop(strlen (nth (setq n (1- n)) attvallst)))
          (setq n -1 attvallst nil) ; if any found will nullify attvallst and stop loop
        ) ; if  
      ) ; while
;      (if (= (nth 3 AttLst) nil) ;;if SPLICE_NUMBER attribute is blank then
      (if (not attvallst) ; if no list exists then must have found at least one empty attribute value
       (progn
;        (vl-remove blck Blklst) ;;remove block from selection set
        (setq newdBlklst (vl-remove blck newdBlklst)) ;;remove block entity with empty attribute value from new entity list
        (alert "removed blanks")
       )
      )
;      (alert "removed blanks")
   )  ; foreach
   (if(length newdBlklst) ; if any entities left in selection set
     (progn
      (setq newout (ssadd)) ; convert entitiy list back to selection set
      (foreach itm newdBlklst
       (ssadd itm newout)
      )
      (sssetfirst nil newout)  
     )
    (sssetfirst nil nil)  
   ) ; if
  ) ; progn
  (princ (strcat "No Dynamic Block found by the Name - " name)) ; else
 ) ; if
) ; defun SelectBlocks

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 7 of 16

Gorra
Advocate
Advocate

Ok, thanks this could be useful. Can it be used to flag one attribute? Each block has 69 attributes and most of them will be blank. I just need to remove the ones with blanks in the SPLICE_NUMBER attribute. It's supposed to be the first attribute before adding coordinates but I suspect I have the list reversed or something else that has the code testing something that always has a value. I will experiment tonight and see if I can work that out.

0 Likes
Message 8 of 16

paullimapa
Mentor
Mentor

If you want to just check if attribute Tag "SPLICE_NUMBER" has an empty value, then replace this:

      (setq n (length attvallst))
      (while (> n 0) ; do while loop to test if there are any empty attribute values
        (if(zerop(strlen (nth (setq n (1- n)) attvallst)))
          (setq n -1 attvallst nil) ; if any found will nullify attvallst and stop loop
        ) ; if  
      ) ; while

with this:

      (setq n (length d)) ; length of attributes list
      (while (> n 0) ; do while loop to find matching attribute tag to test if empty value
        (if(eq "SPLICE_NUMBER" (strcase (vla-get-tagstring (nth (setq n (1- n)) d)))) 
          (if (zerop(strlen (vla-get-textstring (nth n d))))(setq n -1 attvallst nil)) ; if empty value found then nullify attvallst and stop loop
        ) ; if  
      ) ; while

 

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 9 of 16

Gorra
Advocate
Advocate

@paullimapa    So it's not giving any error in this function, and the foreach loops the right number of times, but as it moves on, it still has all the blocks selected (31) instead of just the ones with splice numbers(23). When it stops at a later error, after giving the error code it also lists the number of objects in the selection set -which keeps going up in increments of 31 even though there are still 31 blocks actively selected. 

   In a later stage in the code, I think I have to move the function farther down the process. I need to compare and match the SPLICE_NUMBER fields between the existing blocks and the csv, so I may just leave the blanks in and filter them out then, but I'd like to get this function working anyway so I understand the process.  it may be easiest just to do that here.

0 Likes
Message 10 of 16

paullimapa
Mentor
Mentor
Accepted solution

Well, your code initially selects all blocks and places into Blklst:

 

Blklst (ssget "_X" '((0 . "INSERT")))

 

Then it uses a while loop to go through the Blklst selection set and filters out dynamic blocks matching name "SPLICE CLOSURE" and places that in a new selection set out:

 

(while (setq blck (ssname Blklst (setq n (1+ n))))
        (if (= :vlax-true (vla-get-IsDynamicBlock (vlax-ename->vla-object blck)))
          (if (= (strcase (vla-get-Effectivename (vlax-ename->vla-object blck))) (strcase name))
            (ssadd blck out)
          )
        )
    )

 

Then I revised the rest of your code to now only work with selection set out to create a list of entities saving that into initially duplicate variables newdBlklst & dBlklst:

 

 (if (/= 0 (sslength out))
  (progn
   (setq dBlklst (mapcar 'cadr (ssnamex out))) ;; convert filtered dynamic block only selection set to entity list don't need to vl-remove-if since already filtered that out with while loop above
   (setq newdBlklst dBlklst) ; make a copy of this entity list to use to remove block entities with empty attributes
   (sssetfirst nil out)  
   (foreach blck dBlklst ; BlkLst ;;for each block in filtered entity list
      (setq d (vlax-invoke (vlax-ename->vla-object blck) 'getattributes)) ;;Get attributes
.....

 

As it cycles through each item using the foreach function within the dBlklst, the d variable now holds a list of all attribute objects contained in dynamic block "SPLICE CLOSURE"

Then on my last post this code I included should filter out the dynamic block with an empty string for attribute tag "SPLICE NUMBER":

 

      (setq n (length d)) ; length of attributes list
      (while (> n 0) ; do while loop to find matching attribute tag to test if empty value
        (if(eq "SPLICE_NUMBER" (strcase (vla-get-tagstring (nth (setq n (1- n)) d)))) 
          (if (zerop(strlen (vla-get-textstring (nth n d))))(setq n -1 attvallst nil)) ; if empty value found then nullify attvallst and stop loop
        ) ; if  
      ) ; while

 

Then right before the foreach cycle ends an if statement is placed to test if attvallst exists to either remove the dynamic block entity or keep it in the selection set newdBlklst:

 

      (if (not attvallst) ; if no list exists then must have found at least one empty attribute value
       (progn
        (setq newdBlklst (vl-remove blck newdBlklst)) ;;remove block entity with empty attribute value from new entity list
        (alert "removed blanks")
       )
      )
   )  ; foreach

 

 Finally the last bit of the code creates a new selection set based on the above results called newout which should only include found dynamic block "SPLICE CLOSUREwith an entry in the attribute tag "SPLICE NUMBER":

 

   (if(length newdBlklst) ; if any entities left in selection set
     (progn
      (setq newout (ssadd)) ; convert entitiy list back to selection set
      (foreach itm newdBlklst
       (ssadd itm newout)
      )
      (sssetfirst nil newout)  
     )
    (sssetfirst nil nil)  
   ) ; if

 

So if you want to proceed from this point forward with the newout selection set, one way is to add this variable before the defun closing parenthesis:

 

 ) ; if
 newout
) ; defun SelectBlocks

 

Now in the other part of your code that makes a call to run function SelectBlocks you may want to include something like:

 

(setq ss-dynblk-with-tagvalue (SelectBlocks))

 

ss-dynblk-with-tagvalue is a selection set that now only contains dynamic blocks named "SPLICE CLOSURE" with values in attribute tag SPLICE_NUMBER

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 11 of 16

Gorra
Advocate
Advocate

I see it clearer now, I didn't realize d was the list of attributes - attvallst got changed to d when we went from 'find any blanks' to 'find blank splice number'  but didn't change everywhere. Is that accurate?

0 Likes
Message 12 of 16

paullimapa
Mentor
Mentor

sort of...here's that portion of the code:

      (setq d (vlax-invoke (vlax-ename->vla-object blck) 'getattributes)) ;;Get attributes
      (setq AttLst (cons (append (setq attvallst(mapcar 'vla-get-textstring d))) AttLst)) ;;Compile list of attribute values & place values into a list = attvallst
      (alert "got attributes")

The block is first converted from an entity to a (V)isual(L)isp(A)ctivX-Object or vl obj format using function (vlax-ename->vla-object)

Then function getattributes is applied to the vl obj to get the list of attributes and saved to variable d 

Next function (mapcar 'vla-get-textstring d) grabs the attribute text string values from each item of d and I saved this list in attvallst 

Likewise, I used a similar method when in my later added code I went back to the d variable using function vla-get-tagstring to check to see if the attribute value is empty:

      (setq n (length d)) ; length of attributes list
      (while (> n 0) ; do while loop to find matching attribute tag to test if empty value
        (if(eq "SPLICE_NUMBER" (strcase (vla-get-tagstring (nth (setq n (1- n)) d)))) 
          (if (zerop(strlen (vla-get-textstring (nth n d))))(setq n -1 attvallst nil)) ; if empty value found then nullify attvallst and stop loop
        ) ; if  
      ) ; while

I'm just using variable attvallst so I can then later set this to nil if the dynamic block contains an empty attribute value. This is the flag I'm using to either keep the dynamic block or remove it from the selection set.

 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 13 of 16

Gorra
Advocate
Advocate

@paullimapa Thanks for your patience. Your explanation and code all make sense, and I've been over mine dozens of times making sure they match. Still can't see why it isn't working.

If splice_number is the first attribute, it would be the 0th in the list of d, but the while loop continues while n > 0. Could that be the reason?

0 Likes
Message 14 of 16

paullimapa
Mentor
Mentor

No because n is based on the length of d. Even when there’s only 1 element in d the length would then be 1 to start the whole loop. N will go to 0 after completing the first loop and because n must be > then 0 to continue the while loop at that point it’ll stop. 


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes
Message 15 of 16

Gorra
Advocate
Advocate

Typo. I missed the 'd' in one instance of newdBlklst. Works fine now. 

0 Likes
Message 16 of 16

paullimapa
Mentor
Mentor

Glad you finally got it working…cheers!!!


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes