Change attribute values based on values from AutoCAD table

Change attribute values based on values from AutoCAD table

hbohm
Advocate Advocate
3,938 Views
29 Replies
Message 1 of 30

Change attribute values based on values from AutoCAD table

hbohm
Advocate
Advocate

Hello everyone, I would like to populate an AutoCAD block with attributes with values pulled from an AutoCAD table. I've tried a few different LISP routines and can't figure out how to pair them up.

 

This post from @Anonymous is very close to what I'm after, but I don't want to pull the data from an Excel file:
Find & replace 'Attribute TEXT value' based on input provided in XLS

 

I've found a LISP that extracts the values from each cell of the table and assigns them to a separate variable, but I don't know how to push those values into my block attributes.

 

Here is the code for the lisp that extracts the table cell values. Could I get some help adding on to it please? Any help is appreciated.

 

(defun c:TDE (/ c ctr dat etdata ind lst prefix r tab tblent tblobj val varname)
(setq etdata (entget (setq tblent (car (entsel "\nSelect table object: "))))
ind (cdr (assoc 91 etdata))
tab (cdr (assoc 92 etdata))
r 0 ;;;This determines which row to start with
c 0 ;;;This determines which column to start with
ctr 1 ;;;This is the starting variable # (var1, var2, etc.)
prefix "var"
tblobj (vlax-ename->vla-object tblent))
(while (< c tab)
(while (< r ind)
(set (setq varname (read (strcat prefix (itoa ctr))))
(setq val (vlax-invoke tblobj 'getcellvalue r c)))
(setq lst (cons (setq dat (cons varname val)) lst))
(setq ctr (if (listp (cdr dat))
ctr
(1+ ctr))
r (1+ r)))
(setq r 0
c (1+ c)))
(vl-remove-if '(lambda (x) (listp (cdr x))) (reverse lst)))

 

0 Likes
3,939 Views
29 Replies
Replies (29)
Message 2 of 30

devitg
Advisor
Advisor

@hbohm , 

 

Please upload your sample dwg with ACADTABLE 

 

0 Likes
Message 3 of 30

Sea-Haven
Mentor
Mentor

Big HINT have a look at Lee-mac.com pick cell in table, it does just that select a cell in a table by just clicking inside cell, returns cell address. So could pick range by picking 2 cells. The order when placing in block attributes may not match table order, last comment you can PUT attributes by creation order so don't need tagnames.

 

0 Likes
Message 4 of 30

hbohm
Advocate
Advocate

@devitg I attached the .dwg I am testing the LISP routines on. It has a block called "FUSELABEL" on the left with all the attributes in it. The table on the right is what would typically be generated with AutoCAD Electrical's Component Schematic Report. The only modification I made to the table is I deleted the header row because the sticker block already has that text included in it. 

0 Likes
Message 5 of 30

hbohm
Advocate
Advocate

@Sea-Haven I would be willing to try out Lee Mac's LISP. What is the name of the program you are referring to? I actually have already spliced some of Lee's code together with others to try and make this work, but I have hit a wall and cannot proceed on my own. Thanks

0 Likes
Message 6 of 30

devitg
Advisor
Advisor

@hbohm I made some changes, seem to be the main fact was the line 

 

 

(set (setq varname (read (strcat prefix (itoa ctr))))

 

 

I take off the SET 

Now it return a dotted pair list 

 

(defun c:TDE (/ c ctr dat etdata ind lst prefix r tab tblent tblobj val varname)
(setq etdata (entget (setq tblent (car (entsel "\nSelect table object: ")))))
(setq ind (cdr (assoc 91 etdata)))
(setq tab (cdr (assoc 92 etdata)))
(setq r 0) ;;;This determines which) row to start with
(setq c 0) ;;;This determines which column to start with
(setq ctr 1) ;;;This is the starting variable # (var1, var2, etc.)
(setq prefix "var")
(setq tblobj (vlax-ename->vla-object tblent))
(setq lst ())  
(while (< c tab)
(while (< r ind)
(setq varname (read (strcat prefix (itoa ctr))))
(setq val (vlax-invoke tblobj 'getcellvalue r c))
(setq lst (cons (setq dat (cons varname val)) lst))
(setq ctr (if (listp (cdr dat))
ctr
(1+ ctr))
r (1+ r)))
(setq r 0
c (1+ c)))
(setq var#+value(vl-remove-if '(lambda (x) (listp (cdr x))) (reverse lst))))

 

So var#+value hold all dotted pairs 

 

 

((VAR1 . "FU100") (VAR2 . "FU101") (VAR3 . "FU102") (VAR4 . "FU103") (VAR5 . "FU104") (VAR6 . "FU105") (VAR7 . "KLDR") (VAR8 . "3AG") (VAR9 . "3AG") (VAR10 . "3AG") (VAR11 . "3AG") (VAR12 . "3AG") (VAR13 . "600V") (VAR14 . "250V") (VAR15 . "250V") (VAR16 . "250V") (VAR17 . "250V") (VAR18 . "250V") (VAR19 . "10 AMP") (VAR20 . "2 AMP") (VAR21 . "2 AMP") (VAR22 . "3 AMP") (VAR23 . "2 AMP") (VAR24 . "5 AMP")) 

 

I do not know what you further need

 

 

 

 

 

 

 

 

 

0 Likes
Message 7 of 30

hbohm
Advocate
Advocate

@devitg As you tested, the provided code works fine for getting the table data into variables. What I need next is for the stored data to replace the attribute values in the "FUSELABEL" block. I'm not sure whether the variables are even needed for this to work but since the code was intact I just left it for now.

 

Here are the steps I'm imagining:

1) Run lisp. 

2) Select table to extract data from. 

3) Select the FUSELABEL block, and it should copy the stored data into the appropriate attributes. 

 

It would also be acceptable if the block name "FUSELABEL" was hard-coded into the routine in order to skip the extra user selection prompt. 

0 Likes
Message 8 of 30

Sea-Haven
Mentor
Mentor

I am sure if this is not Lee's code he will let me know the make table part is mine.

 

(vl-load-com)

;; Example adds a table to model space that is 5 rows by 3 columns
;; with a row height of 10 units and column width of 30 units
(defun c:AddTable( / acadObj curDoc insPt mSpaceObj tableObj)
    (setq acadObj (vlax-get-acad-object))
    (setq curDoc (vla-get-ActiveDocument acadObj))

    (setq insPt (vlax-3d-point 0 0 0))

    (setq mSpaceObj (vla-get-ModelSpace curDoc))
    (setq tableObj (vla-Addtable mSpaceObj insPt 5 3 10 30))

    (vla-put-StyleName tableObj "MyTableStyle")
)
; By lee-mac ?
;; Example shows how to pick a single table cell on screen and change its value.
;; This example demonstrates the ActiveX properties/methods HitTest,
;; GetCellType, GetText and SetText.

(defun c:SelectTableCell ( / pick vHeight vWidth lwrLeft uprRight vector
                                           SS_TABLES cnt eMax tableObj row col cellValueOrg)
  
  ;; Ask the user for a point on screen
  (if (/= (setq pick (vlax-3d-point (getpoint "\nSelect Cell to edit: "))) nil)
    (progn

      ;; Get the corners of the screen display to build our selection set
      (setq vHeight (getvar "viewsize"))
      (setq vWidth (* (/ (nth 0 (getvar "screensize")) (nth 1 (getvar "screensize"))) vHeight))

      (setq lwrLeft (list (- (nth 0 (getvar "viewctr")) (/ vWidth 2)) (- (nth 1 (getvar "viewctr")) (/ vHeight  2)) 0))
      (setq uprRight (list (+ (nth 0 (getvar "viewctr")) (/ vWidth 2)) (+ (nth 1 (getvar "viewctr")) (/ vHeight  2)) 0))

      ;; Get the current display orientation
      (setq vector (vlax-make-safearray vlax-vbDouble '(0 . 2)))
      (vlax-safearray-fill vector '(1 1 1))
      (setq vector (vlax-make-variant vector))
      
      ;; Select all the table objects visible on screen
      (if (setq SS_TABLES (ssget "C" lwrleft uprright (list (cons 0 "ACAD_TABLE"))))
        (progn
   
          (setq cnt 0
                eMax (sslength SS_TABLES)
          )

          ;; Step through all the items in the selection set
          (while (> eMax cnt) 
            ;; Geta table object from the selection set
            (setq tableObj (vlax-ename->vla-object (ssname SS_TABLES cnt)))
  
            ;; Return values for what cell was picked in
            (setq row 0
                  col 0)

            ;; Below is also a sample to see if a valid cell is picked
            ;; (vla-select table pick vector vector 5 5 :vlax-false 'row 'col)
     
            ;; Check to see if a valid cell was picked
            (if (= (vla-hittest tableObj pick vector 'row 'col) :vlax-true)
              (progn

                ;; Get out of the loop
                (setq cnt (1+ eMax))
  
                ;; Check to see what the Cell Type is (Text or Block)
                (if (= (vla-GetCellType tableObj row col) acTextCell)
                  (progn
                    ;; Let's get the value out
                    (setq cellValueOrg (vla-GetText tableObj row col))

                    ;; Change the current value
                    (vla-SetText tableObj row col "Revised Text")
                    (vla-Update tableObj)
                    (alert "Cell text was changed.")
          
                    ;; Restore the original value
                    ;(vla-SetText tableObj row col cellValueOrg)
                    ;(vla-Update tableObj)
                    ;(alert "Cell text was changed back to the original value.")
                    ;(setq cnt eMax)
                  )
                )
              )
            )
            (setq cnt (1+ cnt))
          )
        )
      )
    )
  )
 (princ)
)
0 Likes
Message 9 of 30

Sea-Haven
Mentor
Mentor

Part 2 once you read table can do something like this. You need to make lst including all attribute values you have like 68 attributes so need to build a lst that suits can have a textstring like "".

 

Other wise have to loop repeatedly checking attribute Tagname then change.

 

I would leave the heading and description in the table, this will give how many rows and columns.

(setq row  (vla-get-Rows objtable))
(setq col  (vla-get-Columns objtable))

 

(setq x 0)
(foreach att (vlax-invoke (vlax-ename->vla-object blk 'getattributes)
(vla-put-textstring att (nth x lst))
(setq x (1+ x))
)

 

0 Likes
Message 10 of 30

devitg
Advisor
Advisor

@hbohm , the main problem is how to relate the 

;;;((VAR1 . "FU100")
;;;  (VAR2 . "FU101")
;;;  (VAR3 . "FU102")
;;;  (VAR4 . "FU103")
;;;  (VAR5 . "FU104")
;;;  (VAR6 . "FU105")
;;;  (VAR7 . "KLDR")
;;;  (VAR8 . "3AG")
;;;  (VAR9 . "3AG")
;;;  (VAR10 . "3AG")
;;;  (VAR11 . "3AG")
;;;  (VAR12 . "3AG")
;;;  (VAR13 . "600V")
;;;  (VAR14 . "250V")
;;;  (VAR15 . "250V")
;;;  (VAR16 . "250V")
;;;  (VAR17 . "250V")
;;;  (VAR18 . "250V")
;;;  (VAR19 . "10 AMP")
;;;  (VAR20 . "2 AMP")
;;;  (VAR21 . "2 AMP")
;;;  (VAR22 . "3 AMP")
;;;  (VAR23 . "2 AMP")
;;;  (VAR24 . "5 AMP")
;;;)

With the ATT list from the block 

"P_TAG1"
  "DESCRIPTION" "MFG" "CAT" "ASSYCODE" "FAMILY" "WDBLKNAM" "P_ITEM" "FU1" "CLASS1" "V1" "R1" "FU2"
  "CLASS2" "V2" "R2" "FU3" "CLASS3" "V3" "R3" "FU4" "CLASS4" "V4" "R4" "FU5" "CLASS5" "V5" "R5"
  "FU6" "CLASS6" "V6" "R6" "FU7" "CLASS7" "V7" "R7" "FU8" "CLASS8" "V8" "R8" "FU9" "CLASS9" "V9"
  "R9" "FU10" "CLASS10" "V10" "R10" "FU11" "CLASS11" "V11" "R11" "FU12" "CLASS12" "V12" "R12" "FU13"
  "CLASS13" "V13" "R13" "FU14" "CLASS14" "V14" "R14" "FU15" "CLASS15" "V15" "R15"

 

 

0 Likes
Message 11 of 30

hbohm
Advocate
Advocate

@devitg & @Sea-Haven thank you for the responses so far. I will need some time to process this and since Christmas is here it won't be until next week. I'll do what I can and get back to you on Monday. Much appreciated! 

0 Likes
Message 12 of 30

hbohm
Advocate
Advocate

@Sea-Haven I have tried as much as I am capable of and got stuck again. 

 

I found this code to grab the FUSELABEL block tag names after clicking on it. Can I incorporate this into the code you provided? If not, you will have to guide me on how to create a list of all the block attributes in your Part 2.

(defun c:foo (/ e)
(if (and (setq e (car (entsel)))
(vlax-property-available-p (setq e (vlax-ename->vla-object e)) 'hasattributes)
)
(mapcar 'print
(vl-sort (mapcar '(lambda (x) (vla-get-tagstring x)) (vlax-invoke e 'getattributes)) '<)
)
)
(princ)
)
0 Likes
Message 13 of 30

hbohm
Advocate
Advocate

@devitg Once the variables are set to the table values, do you know of a way to set the variables to the attribute tag values?  I have no problem listing all 60 of the attributes into the code, if that's what it requires..

 

Example:

VAR1 = FU1

VAR2 = FU2

....

VAR47 = R11

VAR48 = R12

 

The FUSELABEL attribute tag names will never change and the report table format will never change so the order should remain the same. FU1 will always equal the VAR1 value, R12 will always equal the VAR48 value, etc.

0 Likes
Message 14 of 30

hbohm
Advocate
Advocate

@devitg or @Sea-Haven Any update to this? I cannot proceed on my own. I'm also open to new suggestions if attributes are too hard to work with. In the end all I need is the built-in AutoCAD report values to populate our fuse label block that we use to print a sticker out and apply it to our panel once it's built and tested in the shop. I would just use the report and call it good, but the sticker is formatted to size and has our company logo incorporated in it.

0 Likes
Message 15 of 30

devitg
Advisor
Advisor

@hbohm why not to use ATTOUT, edit the fuseinfo.txt, and ATTIN ?

 

 

0 Likes
Message 16 of 30

hbohm
Advocate
Advocate

Thanks for the suggestion, but I don't see this as an improvement. We already manually type all 60 values as it is. Plus the format of the .txt file is way more confusing than just using the Enhanced Attribute Editor after double-clicking on the block. We're just trying to reduce the amount of time it takes to enter the data into the block. Since AutoCAD already provides the report, I figured it would be great to grab the data from there instead of retyping it all by hand, hence the LISP routine idea.

0 Likes
Message 17 of 30

Sea-Haven
Mentor
Mentor

If the table order is the same as the attribute creation order note not visual then its easy 

 

(setq attnum -1)
(foreach att (vlax-invoke (vlax-ename->vla-object blk 'getattributes)
             (vla-put-textstring att (nth (setq attnum (1+ attnum)) lst))
              )
)

Your list 'lst' is all the table values, the blk is a VL picked block object.

 

0 Likes
Message 18 of 30

hbohm
Advocate
Advocate

@Sea-Haven I'm real close to making this work. I finished the code to create my "lst" of values from the table. The question I have now is, how do I pass the "blk" selection to your most recent code snippet?

 

Here is what I tried, but I get "error: too many arguments"

(setq blk (car (entsel)))
(setq attnum -1)
(foreach att (vlax-invoke (vlax-ename->vla-object blk 'getattributes)
             (vla-put-textstring att (nth (setq attnum (1+ attnum)) lst))
              )
)

You said it has to be a VL selected block, is that what my code is above? ENTSEL is the only way I know how to prompt a user to select an object.

0 Likes
Message 19 of 30

Sea-Haven
Mentor
Mentor
Accepted solution

Vl is a little different your very close, you need to make a VL object.  

 

(setq blk  (vlax-ename->vla-object (car (entsel "Pick blk"))))

selection set

(repeat (setq x (sslength ss))
(setq blk (vlax-ename->vla-object (ssname ss (setq x (- x 1))))) 
0 Likes
Message 20 of 30

hbohm
Advocate
Advocate

@Sea-Haven I don't know what's going on now. When it gets to the FOREACH section it still gives me the "too many arguments" error.

 

Here is my full code:

(defun c:FuseSticker  (/ c etdata ind lst r tab tblent tblobj val blk att attnum)
  
 ;;Select table, then create list of cell values
 (setq etdata (entget (setq tblent (car (entsel "\nSelect Table Object: "))))
       ind    (cdr (assoc 91 etdata))
       tab    (cdr (assoc 92 etdata))
       r      0   ;;This determines which row to start with
       c      0   ;;This determines which column to start with
       tblobj (vlax-ename->vla-object tblent))

  (while (< c tab)
    (while (< r ind)
      (setq val (vlax-invoke tblobj 'getcellvalue r c))
      (setq lst (cons val lst))
      (setq r   (1+ r)))
    (setq r 0
        c (1+ c)))
  (reverse lst)

  ;;Select block, then change attributes to match table values
  (setq blk  (vlax-ename->vla-object (car (entsel "Select Fuse Sticker: "))))
  (setq attnum -1)  
  (foreach att (vlax-invoke (vlax-ename->vla-object blk 'getattributes)
		 (vla-put-textstring att (nth (setq attnum (1+ attnum)) lst))))  
    
  ) ;;defun

 

I reattached my test .dwg with the block and table in it.

0 Likes