Read Specific Attribute Tag from Specific Block (no selection)

Read Specific Attribute Tag from Specific Block (no selection)

ebsoares
Collaborator Collaborator
5,327 Views
17 Replies
Message 1 of 18

Read Specific Attribute Tag from Specific Block (no selection)

ebsoares
Collaborator
Collaborator

Hi, everyone.

Would anyone be able to help me create a lisp that looks in the drawing for a specific block, and if it exists then looks for a specific attribute tag in that block, and if it exists AND is not empty stores that info into a variable to be used later?

I've found multiple posts online that do that but start by asking the user to select a block in the drawing, whereas what I am needing is to just call a lisp and, if said block with said attribute exists, get that string variable created/populated (not interested in having to click on anything).

For a sample, the attached drawing contains a block named "NAVFAC_Sheet_Info" and an attribute tag named "SHEET_TITLE_1_OF_1"

Any help would be greatly appreciated Smiley Happy

Edgar

0 Likes
Accepted solutions (3)
5,328 Views
17 Replies
Replies (17)
Message 2 of 18

serag.hassouna
Advocate
Advocate

I guess this approach would be tackled with something like that

(vl-load-com)
(setq blkname (getstring "\nSpecify Block Name: "))
(princ)
(setq block (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blkname)) ;get block's vla-object

(vlax-for obj block
  (if (wcmatch (vla-get-objectname obj) "AcDbAttribute*")
    (progn
      (setq tagstr (vla-get-tagstring block)) ;get Tag String of the attribute
      (setq textstr (vla-get-textstring block)) ;get Text String of the attribute
    );progn [then part]
  );if the object is an attribute tag
);vlax-for

 

0 Likes
Message 3 of 18

ebsoares
Collaborator
Collaborator

Thanks for the reply, @serag.hassouna - I'll give this a try on Monday then and let you know how it went Smiley Very Happy

0 Likes
Message 4 of 18

devitg
Advisor
Advisor

Please try it

 

;;**************************************************************************************

(DEFUN GETALLATTRIBUTES-01  (OBJSELECTION /)
  (IF (= (TYPE OBJSELECTION) 'ENAME)
    (SETQ OBJSELECTION (VLAX-ENAME->VLA-OBJECT OBJSELECTION))
    )
  (IF (VLAX-PROPERTY-AVAILABLE-P OBJSELECTION "hasattributes")
    (IF (= (VLA-GET-HASATTRIBUTES OBJSELECTION) :VLAX-TRUE)
      (VLAX-SAFEARRAY->LIST
        (VARIANT-VALUE
          (VLA-GETATTRIBUTES OBJSELECTION)
          )
        )
      )
    )
  )


(defun setq-tag-string ()


(setq block-named "NAVFAC_Sheet_Info" )
(setq attribute-tag-named "SHEET_TITLE_1_OF_1")

(setq inserted-block-obj(VLAX-ENAME->VLA-OBJECT(ssname  (ssget "X" (list ( cons 0 "INSERT") (cons 2  "NAVFAC_Sheet_Info" )))0)))
      
(setq alltributes-list (GETALLATTRIBUTES-01 inserted-block-obj ))
(Foreach  attribute alltributes-list

 (if (= (VLA-GET-TAGSTRING attribute)  attribute-tag-named)   

     (setq att-value (vla-get-textstring attribute))
);end if 
  );end vlax
  att-value
 );END  setq-tag-string

(setq att-value (setq-tag-string))

No error check  , all variable global. 

Message 5 of 18

_gile
Consultant
Consultant
Accepted solution

Hi,

 

You can use these routines:

 

 

(defun gc:getreferences	(bname / blk)
  (if (setq blk (tblobjname "BLOCK" bname))
    (vl-remove-if-not
      (function (lambda (b) (entget b)))
      (gc:massoc 331 (entget (cdr (assoc 330 (entget blk)))))
    )
  )
)

(defun gc:massoc (code alst)
  (if (setq alst (member (assoc code alst) alst))
    (cons (cdar alst) (gc:massoc code (cdr alst)))
  )
)

(defun gc:getattvalue (ent tag / lst val)
  (while (and (not val)
	      (setq lst (entget (entnext ent)))
	      (= "ATTRIB" (cdr (assoc 0 lst)))
	 )
    (if	(= (strcase tag) (cdr (assoc 2 lst)))
      (setq val	 (cdr (assoc 1 lst)))
      (setq ent (cdr (assoc -1 lst)))
    )
  )
  val
)

and the following expression will return a list containing the value of "SHEET_TITLE_1_OF_1" attribute for all inserted block named "NAVFAC_Sheet_Info" (because there might be more than one).

(mapcar
  (function (lambda (x) (gc:getattvalue x "SHEET_TITLE_1_OF_1")))
  (gc:getreferences "NAVFAC_Sheet_Info")
)

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 18

serag.hassouna
Advocate
Advocate

@ebsoares
Well, give this a try, I've debugged & modified my initial code snippet & make it as a routine to return the value you need.
There're 2 types of things to be returned

  1. Text Strings
  2. Prompt Strings

the routine's syntax is

(getblkattr attr_type)

where attr_type is either "text" to get the text string, or "prompt" to get the prompt string.

 

Here's the routine

(defun getblkattr (attr_type / blkname tag_str block obj tagstr textstr promptstr)
  (vl-load-com)
  (setq blkname (getstring "\nSpecify Block Name: "))
  (setq tag_str (getstring "\nSpecify Tag Value: "))
  (princ)
  (setq block (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) blkname)) ;get block's vla-object

  (vlax-for obj block
    (if (wcmatch (vla-get-objectname obj) "AcDbAttribute*")
      (progn
        (if
	  (wcmatch (setq tagstr (vla-get-tagstring obj)) tag_str)
	  (progn
	    (setq textstr (vla-get-textstring obj));get text string
	    (if
	      (vlax-property-available-p obj 'PromptString)
	      (setq promptstr (vla-get-promptstring obj))
	      );if [getting prompt value if it exists]
	    );progn
	  );if [getting the text string for a specific tag value]
      );progn [then part]
    );if the object is an attribute tag
  );vlax-for

  (cond
    ((wcmatch attr_type "text") textstr)
    ((wcmatch attr_type "prompt") promptstr)
    (t nil)
    );cond [of the value to be returned]
  
);defun [getblkattr]

& here is the results from applying this routine on your sample drawing on the command prompt

Command: (getblkattr "text")

Specify Block Name: NAVFAC_Sheet_Info

Specify Tag Value: SHEET_TITLE_1_OF_1
"SHEET TITLE"

Command: (getblkattr "prompt")

Specify Block Name: NAVFAC_Sheet_Info

Specify Tag Value: SHEET_TITLE_1_OF_1
"SHEET TITLE (1 OF 1)"

 

Message 7 of 18

_gile
Consultant
Consultant

@serag.hassouna, your routine inspect the block definition (AcDbBlockTableRecord) so when asking for the TextString, it returns the block definition default value.

 

As @devitg, I think the OP is about the attribute value (text string) of an inserted block reference (AcDbBlockReference).

The differences between @devitg reply and mine are:

- @devitg returns the attribute value of the first found block reference, I return a list of the attribute values for all inserted block references.

- @devitg uses a selection set (ssget "_X" ...), I directly get the references from the block table record data which is much more efficient than iterating the whole database (what (ssget "_X" ...) does).

- @devitg example will throw an error if none block reference is found (because ssget will return nil), mine will return nil.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 8 of 18

dbhunia
Advisor
Advisor

@ebsoares wrote:

Hi, everyone.

.......

block in the drawing, whereas what I am needing is to just call a lisp and, if said block with said attribute exists, get that string variable created/populated (not interested in having to click on anything).

 

.......


 

Hi,

 

Try this code....

 

It will ask you two thing "Block Name" & "Attribute Tag Name" (put this values as per your Block/Attribute details & both are case sensitive)

 

Then the code will give you two things....

 

1.   A single string value/values of Attribute/Attributes (Depends on No. of Blocks in Drawing, each Attribute value is separated by space).

2.   A single List of value/values of Attribute/Attributes (Depends on No. of Blocks in Drawing, each Attribute value is separated by inverted commas).

 

If the Drawing do not the contains the Input "Block Name" or "Attribute Tag Name" it will give a message "Input Block/Tag Name not Found"

 

(It has been tested in "AutoCAD 2007", hope fully it will work in latest version. Otherwise you have to wait till Monday)


Debashis Bhunia
Co-Founder of Geometrifying Trigonometry(C)
________________________________________________
Walking is the First step of Running, Technique comes Next....
0 Likes
Message 9 of 18

dbhunia
Advisor
Advisor
Accepted solution

Today I tested the code in AutoCAD 2017 on your given Drawing, I think you need The attached code.......

 

It works on multiple Blocks in the Drawing (and Depends on No. of Blocks in Drawing, each Attribute value is separated by comma).

 

If the Drawing do not the contains the Input "Block Name" or "Attribute Tag Name" it will give a message "Input Block/Tag Data not Found"


Debashis Bhunia
Co-Founder of Geometrifying Trigonometry(C)
________________________________________________
Walking is the First step of Running, Technique comes Next....
Message 10 of 18

ebsoares
Collaborator
Collaborator

Wow - you guys all rock! Great replies.

I'm still on the fence between @_gile's and @dbhunia's responses, as they both got exactly what I am looking for.

Again, I really appreciate the time all of you put into helping me.

Edgar

0 Likes
Message 11 of 18

serag.hassouna
Advocate
Advocate

@_gile That's right, the reason why I've chosen to deal with the block definition is that it ALWAYS exists, however, there would not be any block reference object from the specified block definition in the drawing.
But I have to note that my routine assumes that the block definition has only 1 attribute that has the specified tag name, even though the routine will not crash if there're many attribute objects within the block definition (& with the same tag name), & I guess it will return the default values for the lastly inserted attribute within that block definition.

0 Likes
Message 12 of 18

ebsoares
Collaborator
Collaborator

Hi, @_gile.

I was wondering if you could help me with a couple of refinements:

 

1) In the gc:getreferences function you used an if function ("if the block is found do this"), so I tried to use it to also act in the case the block was not found ("NAVFAC_Sheet_Info" in the example) and warn the user, such as this:

 

(if (setq blk (tblobjname "BLOCK" bname))
    (vl-remove-if-not
        (function (lambda (b) (entget b)))
        (gc:massoc 331 (entget (cdr (assoc 330 (entget blk)))))
    )
    (princ "\nBlock not found")
)

Problem is, when the block is not found AutoCAD gives me this error: bad argument type: listp "\nBlock not found" - Do you know how to solve this issue?

 

2) Similarly, in the gc:getattvalue function, if the block is found, but there's no attribute of the name the user provided ("SHEET_TITLE_1_OF_1" in the example), I'd like to warn the user as well, like the code below:

 

(defun gc:getattvalue (ent tag / lst val)
    (while
        (and (not val)
            (setq lst (entget (entnext ent)))
            (= "ATTRIB" (cdr (assoc 0 lst)))
        )
        (if (= (strcase tag) (cdr (assoc 2 lst)))
            (setq val (cdr (assoc 1 lst)))
            (setq ent (cdr (assoc -1 lst)))
        )
    )
; if val = nil, then the attribute was not found (?) - stop function and warn user: (if (not val) (princ "\nNo such attribute") val ) )

Problem this time is that, if the attribute is not found, the routine still goes on to rename the layout tab with the string "No such attribute" (which is kinda of funny Smiley LOL), instead of stopping the function and warning the user - could you possibly help me here as well?

 

 

Any help would be awesome!

Thanks,

Edgar

0 Likes
Message 13 of 18

dbhunia
Advisor
Advisor

Hi

 

This is a little modification of my previous Code..........As per your requirements......

 

First it check the "BLOCK" existence then it will check for "ATTRIBUTE" existence.......

 

Try this......


Debashis Bhunia
Co-Founder of Geometrifying Trigonometry(C)
________________________________________________
Walking is the First step of Running, Technique comes Next....
Message 14 of 18

_gile
Consultant
Consultant

Here's an example:

 

;; check if the block table contains the block
(if (tblobjname "BLOCK" "NAVFAC_Sheet_Info")
  ;; check if the block is referenced
  (if (setq references (gc:getreferences "NAVFAC_Sheet_Info"))
    ;; get the attribute values of block references
    (if (setq attValues (mapcar
                          (function (lambda (x) (gc:getattvalue x "SHEET_TITLE_1_OF_1")))
                          references
                        )
        )
      ;; do your stuff with found values
      (foreach val attValues (strcat "\n" val))
      (alert "None attribute found")
    )
    (alert "None reference found")
  )
  (alert "Block not found in the block table")
)


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 15 of 18

cadffm
Consultant
Consultant

If you are using the gc:getattvalue function, i would change the tag comparing

from  (if    (= (strcase tag) (cdr (assoc 2 lst)))

to => (if    (= (strcase tag) (strcase (cdr (assoc 2 lst))))

or => (if    (= tag (cdr (assoc 2 lst)))

because it is possible to have Attrib&AttdefTags with lower case characters.

In all cases, it's also a question of how you like to handle duplicate attribute(tags).

 

Sebastian

Message 16 of 18

ebsoares
Collaborator
Collaborator

Thanks for the replies, everyone!

@_gileI tried the edits you provided and both the block definition and reference portions work well, but I couldn't get the check on the attribute tag name to work. If, for example, I change the attribute tag name to "SHEET_TITLE_1_OF_foo" (which does not exist) the if function still returned true and it never got to the portion (alert "None attribute found").

Just so you know, to test I just replaced the (foreach val attValues (strcat "\n" val)) line with (alert "Do stuff").

Any chance I am missing something?

0 Likes
Message 17 of 18

_gile
Consultant
Consultant
Accepted solution

Sorry, try this way:

(if (tblobjname "BLOCK" "bloc-att")
  ;; check if the block is referenced
  (if (setq references (gc:getreferences "bloc-att"))
    ;; get the attribute values of block references
    (if (setq attValues
               (vl-remove nil
                          (mapcar
                            (function (lambda (x) (gc:getattvalue x "SHEET_TITLE_1_OF_1")))
                            references
                          )
               )
        )
      ;; do your stuff with found values
      (foreach val attValues (princ (strcat "\n" val)))
      (alert "None attribute found")
    )
    (alert "None reference found")
  )
  (alert "Block not found in the block table")
)

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 18 of 18

ebsoares
Collaborator
Collaborator

Fantastic! Thanks, @_gile!!!

0 Likes