Author Topic: Entering Attribute Block Definition to get strings and tags  (Read 6192 times)

0 Members and 1 Guest are viewing this topic.

Coder

  • Swamp Rat
  • Posts: 827
Entering Attribute Block Definition to get strings and tags
« on: March 02, 2011, 08:24:13 AM »
Hello.

Hope that someone would guide me about the way to get into Attribute Block Definition to extract its strings and tags .

Here is my start with it ....

Code: [Select]
(vl-load-com)

(setq sel (ssget '((0 . "INSERT")(66 . 1))))

  (vlax-for blk (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
    (vlax-for block (vla-item sel blk)
      ......

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #1 on: March 02, 2011, 08:28:56 AM »
If you are looking to retrieve attribute strings from an Insert, i.e. those attribute values pertaining to a specific instance of a block, use the GetAttributes method on the Block Reference Object (Insert entity), or step through the attribute entities using entnext on the Insert entity.

These will help you:

http://lee-mac.com/attributefunctions.html

Coder

  • Swamp Rat
  • Posts: 827
Re: Entering Attribute Block Definition to get strings and tags
« Reply #2 on: March 02, 2011, 11:12:00 AM »
Thank you .

But I am not sure where should I start from ?
And how to collect codes together ....

appreciated

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #3 on: March 02, 2011, 11:24:35 AM »
Start with the 'GetAttributes' subfunction, and follow the example calling function for directions on how to call it.  :-)

Coder

  • Swamp Rat
  • Posts: 827
Re: Entering Attribute Block Definition to get strings and tags
« Reply #4 on: March 02, 2011, 12:24:59 PM »
I am sorry for not being able to deal with your valued codes , although I do not like the use of single selection set and that's why I wrote
something instead and hope you would correct it for me please .

Code: [Select]
(setq sel (ssget '((0 . "INSERT") (66 . 1))))
(vlax-for blk (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
  (vlax-for block (vla-item (vlax-ename->vla-object (ssname sel 0)) blk)
    (if (eq block (vla-get-objectname "AcDbBlockReference"))
      (print (vla-get-TextString block))
    )
  )
)

Thanks

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #5 on: March 02, 2011, 12:29:40 PM »
Only the first line is correct, the rest is going in the wrong direction, consider this calling function:

Code: [Select]
(defun c:test ( / ss i ) (vl-load-com)

  (if (setq ss (ssget '((0 . "INSERT") (66 . 1))))
    (repeat (setq i (sslength ss))
      (print (LM:GetAttributes (vlax-ename->vla-object (ssname ss (setq i (1- i))))))
    )
  )

  (princ)
)

The attribute tags and values are part of the Block Reference, not the Block Definiton as found in the Block Table.

Coder

  • Swamp Rat
  • Posts: 827
Re: Entering Attribute Block Definition to get strings and tags
« Reply #6 on: March 02, 2011, 12:53:35 PM »
That's really nice . Thank you .

I need a clarification about  (lambda ( attrib ) and this (vlax-invoke block 'GetAttributes) !!

How does lambda work with (attrib) within your codes ? does it mean x factor ?
And how did the routine support this unknown variable?

Code: [Select]
(defun LM:GetAttributes ( block )
  ;; © Lee Mac 2010
  (mapcar
    (function
      (lambda ( attrib )
        (cons (vla-get-TagString attrib ) (vla-get-TextString attrib))
      )
    )
    (vlax-invoke block 'GetAttributes)
  )
)

Great help from you Lee.

Thanks a lot.

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #7 on: March 02, 2011, 03:11:24 PM »
Firstly, GetAttributes is a method applicable to a VLA Block Reference. To implement a method on a VLA-Object we could go down one of three routes:

1) vla-[method]

Structure:

Code: [Select]
(vla-[color=green][method][/color] [color=red]<Object>[/color] [color=blue][Param1] [Param2] ... [ParamN][/color])
e.g.

Code: [Select]
(vla-[color=green]GetAttributes[/color] [color=red]<VLA-BlockReference>[/color])
Where 'Object' is the VLA-Object to which the method appies, and 'Param1,...,ParamN' are parameters which may be required by the method which bear some consequence on the outcome of the method.

This implementation of the method returns data in the form of a Variant, whose contents (perhaps a [Safe]Array) would need to be extracted (using vlax-variant-value), and this [Safe]Array would then need to be converted to a List (using vlax-safearray->list).

2) vlax-invoke-method

Structure:

Code: [Select]
(vlax-invoke-method [color=red]<Object>[/color] [color=green]<Method>[/color] [color=blue][Param1] [Param2] ... [ParamN][/color])
e.g.

Code: [Select]
(vlax-invoke-method [color=red]<VLA-BlockReference>[/color] [color=green]'GetAttributes[/color])
Similar to the above route, however the method is supplied as an argument to the function vlax-invoke-method (in the first example the method has its own function).

This route will also return any data in the form of a variant and a similar procedure would need to be followed to convert it to a native LISP data type (such as a List).

3) vlax-invoke

Structure:

Code: [Select]
(vlax-invoke [color=red]<Object>[/color] [color=green]<Method>[/color] [color=blue][Param1] [Param2] ... [ParamN][/color])
e.g.

Code: [Select]
(vlax-invoke [color=red]<VLA-BlockReference>[/color] [color=green]'GetAttributes[/color])
This is an undocumented function, and has a structure similar to the previous example. The difference is that this method will return any data in a native LISP data type (a List), instead of a Variant, so no conversion is required.



Back to my code, where I have used:

Code: [Select]
(vlax-invoke [color=red]block [/color]'[color=green]GetAttributes[/color])
'block' is the VLA Block Reference, and GetAttributes is the method to be implemented on such object. GetAttributes is expressed as a quoted symbol, but this could also be expressed as a string (similar to how the setvar/getvar functions can be used).

And so, should the block have attributes, the above will return a list of the attribute objects, something like:

Code: [Select]
(<VLA Attribute1> <VLA Attribute2> ... <VLA AttributeN>)


Now, many seem to find mapcar difficult to understand, but persevere, since it is a very useful function.

mapcar will take a function argument and a number of list arguments. The number of lists must equal the number of arguments required by the supplied function.

When evaluated, mapcar will evaluate the function on every item of the supplied list(s), and return a list of the result of each evaluation. mapcar will stop evaluation as soon as a list is exhausted.

Note that the function can be any function, taking any number of arguments. lambda describes an anonymous function (usually used for a one-off usage, hence not warranting the overhead of a 'defun'), and, as a function, can be supplied as an argument to mapcar.

A few examples:

Code: [Select]
;; Add 1 to every element of a list:
([color=blue]mapcar [/color]'[color=red]1+[/color] '[color=green](1 2 3 4 5)[/color])
==>  '(2 3 4 5 6)

Code: [Select]
(defun [color=red]AddTwo [/color]( x ) (+ x 2))

([color=blue]mapcar [/color]'[color=red]AddTwo [/color]'[color=green](1 2 3 4 5)[/color])
==> (3 4 5 6 7)

([color=blue]mapcar [/color]'[color=red](lambda ( x ) (+ x 2))[/color] [color=green]'(1 2 3 4 5)[/color])
==> (3 4 5 6 7)

Code: [Select]
([color=blue]mapcar [/color]'[color=red]+[/color] '[color=green](1 2 3 4 5) '(3 4 5 6 7)[/color])
==> (4 6 8 10 12) = ((+ 1 3) (+ 2 4) (+ 3 5) (+ 4 6) (+ 5 7))

Code: [Select]
(defun [color=red]foo [/color]( string number ) (strcat string (itoa number)))

([color=blue]mapcar [/color]'[color=red]foo [/color][color=green]'("a" "b" "c") '(1 2 3)[/color])
("a1" "b2" "c3")

([color=blue]mapcar [/color]'[color=red](lambda ( string number ) (strcat string (itoa number)))[/color] '[color=green]("a" "b" "c") '(1 2 3)[/color])
("a1" "b2" "c3")

Similarly a lambda function can be used with the apply function:

Code: [Select]
(defun [color=red]averageof2 [/color]( num1 num2 ) (/ (+ num1 num2) 2.))

([color=blue]apply [/color]'[color=red]averageof2 [/color]'[color=green]( 1 2 )[/color])
==> 1.5

([color=blue]apply[/color][color=red] '(lambda ( num1 num2 ) (/ (+ num1 num2) 2.))[/color] '[color=green]( 1 2 )[/color])
==> 1.5

Or a general average function:

Code: [Select]
(defun [color=blue]average [/color]( lst ) (/ ([color=blue]apply [/color]'[color=red]+[/color] lst) (float (length lst))))

(average '( 1 2 3 4 ))
==>  2.5



Back to my code:

I use a lambda function taking a single argument (a VLA attribute object), returning a dotted pair of the TagString and TextString properties for the VLA Attribute Object.

I could equally have defined a separate function:

Code: [Select]
(defun [color=red]_TagText[/color] ( attrib )
  (cons (vla-get-TagString attrib ) (vla-get-TextString attrib))
)

([color=blue]mapcar [/color]'[color=red]_TagText[/color] [color=green](vlax-invoke block 'GetAttributes)[/color])

However, since I'm only using the function for this expression, it seems a little overkill to define a whole function for it. Also, using the lambda function improves the readability of the code since the intention of the function is expressed in the same statement and not elsewhere in the code.

As an overview, think about the code in this way:

Code: [Select]
([color=blue]mapcar[/color]
[color=red]  (function
    (lambda ( attrib )
      (cons (vla-get-TagString attrib ) (vla-get-TextString attrib))
    )
  )[/color]
[color=green]  (<VLA-Attribute1> <VLA-Attribute2> ... <VLA-AttributeN>)[/color]
)

Code: [Select]
(
  (cons (vla-get-TagString <VLA-Attribute1>) (vla-get-TextString <VLA-Attribute1>))
  (cons (vla-get-TagString <VLA-Attribute2>) (vla-get-TextString <VLA-Attribute2>))
  ...
  (cons (vla-get-TagString <VLA-AttributeN>) (vla-get-TextString <VLA-AttributeN>))
)

Returning:

Code: [Select]
(
  (<Tag1> . <Text1>)
  (<Tag2> . <Text2>)
  ...
  (<TagN> . <TextN>)
)

A quick note on the use of the function function: this just declares the argument as a function. The use of the apostrophe to indicate that the supplied functional argument is not to be evaluated (but instead taken as an argument for mapcar/apply etc), has the same result as using the function function - with perhaps a small performance improvement by using the function function since the interpreter doesn't have to 'make sense' of the quoted expression.



If you still have questions about my explanation, please ask. But I would ask that you spend some time analyzing my examples to understand each one in turn before asking questions.

Also, I would suggest that you read about passing arguments to functions, and similarly writing functions taking a number of arguments.

Hope this helps!

Lee

LE3

  • Guest
Re: Entering Attribute Block Definition to get strings and tags
« Reply #8 on: March 02, 2011, 03:18:51 PM »
^
Holy s###t man you almost wrote a full chapter that can go into a book....  :lol: wow.

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #9 on: March 02, 2011, 03:21:15 PM »
^
Holy s###t man you almost wrote a full chapter that can go into a book....  :lol: wow.

 :lol: I got a bit carried away  :lol:

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #10 on: March 05, 2011, 09:42:28 AM »
Did my explanation help at all Coder? Or was I wasting my time?

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Entering Attribute Block Definition to get strings and tags
« Reply #11 on: March 05, 2011, 10:47:59 AM »
Thanks Lee,

Replies like these are very informational and useful.

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #12 on: March 05, 2011, 11:05:06 AM »
Replies like these are very informational and useful.

Thanks Jeff, happy you found it comprehensible.  :-)

Coder

  • Swamp Rat
  • Posts: 827
Re: Entering Attribute Block Definition to get strings and tags
« Reply #13 on: March 06, 2011, 06:54:54 AM »
was I wasting my time?

Of course not a matter of wasting time at all Mr. Lee. it's just because of may brain breakdown sometimes  :-D.

Lambda function is well know to me now due to your great favor to me with your explanations  . :-)

I do highly appreciate your hard work for me , and I feel shy due to being unable to implement your idea in a different way
but with not even a bit of chance .  :cry:

Failure Examples ....
Code: [Select]
(vl-load-com)
(if (setq ss (ssget '((0 . "INSERT") (66 . 1))))
    (repeat (setq i (sslength ss))
      (setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
      (setq e (vlax-invoke obj 'GetAttributes))
      (foreach atts e
        (cons (vla-get-TagString attrib ) (vla-get-TextString attrib))))
  (princ))
 

Or this ...

Code: [Select]
(vl-load-com)
(if (setq ss (ssget '((0 . "INSERT") (66 . 1))))
    (repeat (setq i (sslength ss))
      (setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
      (setq e (vlax-invoke obj 'GetAttributes))
        (setq st (vla-get-TextString e))
  )
  (princ)
 

I guess the Block is not a vla-object or so I do not know frankly .....

Please forgive my lazy understanding about this issue .



Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Entering Attribute Block Definition to get strings and tags
« Reply #14 on: March 06, 2011, 07:42:04 AM »
Not bad! A nice attempt, with only a few tweaks:

Code: [Select]
(defun c:test ( / ss i obj e lst ) (vl-load-com)

  (if (setq ss (ssget '((0 . "INSERT") (66 . 1))))
    (repeat (setq i (sslength ss))
      (setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
      (setq e (vlax-invoke obj 'GetAttributes))
      (foreach atts e
        [color=red](setq lst (cons[/color] (cons (vla-get-TagString [color=red]atts[/color]) (vla-get-TextString [color=red]atts[/color])) [color=red]lst))[/color][color=green]
        ;; Added to create a list of all the attribute tags and strings[/color]
      )
    )
  )
[color=red]  (print lst)[/color] [color=green];; Just to print an output[/color]
  (princ)
)


Code: [Select]
(defun c:test ( / ss i obj e ) (vl-load-com)
  
  (if (setq ss (ssget '((0 . "INSERT") (66 . 1))))
    (repeat (setq i (sslength ss))
      (setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
      (setq e  (vlax-invoke obj 'GetAttributes))
      [color=red](print [/color](vla-get-TextString [color=red](car e)[/color])[color=red])[/color] [color=green];; Added so that you can see an output for the example[/color]
      [color=green];; 'car' will return the first element of a list, hence the first Attribute Object.[/color]
    )
  )
  (princ)
)
« Last Edit: March 06, 2011, 07:47:46 AM by Lee Mac »