Jump to content
Ranger-uk

Transfer Attributes from one block to another

Recommended Posts

Ranger-uk

I've been looking for a routine that will allow me to transfer an attribute from one block to another. Ideally I'd like to click on one block which contains the attribute I want (from a field called ID_1), then click on another block and copy the attribute to another specific field (ID_2). I need to do this to quite a few blocks so I'd like to just continue to click between the two types of block.

 

I thought I could modify the code on this page http://www.cadtutor.net/forum/showthread.php?32085-Need-Lisp-to-update-attributes, but this appears to place the same attribute on every block I click. Any help is greatly appreciated!

Share this post


Link to post
Share on other sites
Lee Mac

Or a quick one, more specific:

 

(defun c:ca ( / _SelectBlockWithTag a b des src tag ) (vl-load-com)

 (setq src "ID_1"  ; Source Attribute Tag
       des "ID_2"  ; Destination Attribute Tag
 )

 (defun _SelectBlockWithTag ( tag / e a ) (setq tag (strcase tag))
   (while
     (progn (setvar 'ERRNO 0) (setq e (car (entsel (strcat "\nSelect Block with attribute " tag ": "))))
       (cond
         ( (= 7 (getvar 'ERRNO))
           (princ "\nMissed, Try Again.")
         )
         ( (not e)
           nil
         )
         ( (and
             (eq "INSERT" (cdr (assoc 0 (entget e))))
             (= 1 (cdr (assoc 66 (entget e))))
           )
           (if
             (not
               (setq a
                 (vl-some
                   (function
                     (lambda ( x )
                       (if (eq tag (strcase (vla-get-tagstring x))) x)
                     )
                   )
                   (vlax-invoke (vlax-ename->vla-object e) 'getattributes)
                 )
               )
             )
             (princ (strcat "\nBlock does not contain tag " tag "."))
           )
         )
         ( (princ "\nInvalid Object Selected.") )
       )
     )
   )
   a
 )

 (while
   (and
     (setq a (_SelectBlockWithTag src))
     (setq b (_SelectBlockWithTag des))
   )
   (vla-put-textstring b (vla-get-textstring a))
 )
 (princ)
)

Share this post


Link to post
Share on other sites
Ranger-uk

Wow, that was a fast reply. Thanks, that is perfect, exactly what I was after! :)

Share this post


Link to post
Share on other sites
Lee Mac

You're welcome Ranger-uk :)

 

Nice to see a fellow countryman around here :)

Share this post


Link to post
Share on other sites
BIGAL

Just a bit more same subject Lee was going to ask I used VBA to do this problem but use the database position of the attribute in the block not its tag to change its value then it will always work even if the tag is changed. eg attrib(0) is the first attribute record in a block attrib(1) is next and so on.

so a change of value would be

attribs(1).TextString = txtx1

attribs(2).TextString = TXTY1

 

I was trying to find the same but in plain lisp or Vl code due to vba possibly disapearing. Please no suggestions re .NET

Share this post


Link to post
Share on other sites
Lee Mac

Hi BIGAL,

 

If I understand your question correctly, you can use the positions of the attribute objects in the list returned by:

 

(vlax-invoke <Block Object> 'getattributes)

 

Let me know if you need an example, or clarification.

 

Lee

Share this post


Link to post
Share on other sites
irneb

Or you can do it the old-fashioned way (without vla) by using entnext n-times after the block reference ename. That should give you the nth attribute's ename and then use entget on that. E.g.:

(defun getNthAttrib (blk n / en ed)
 (setq en (entnext blk))
 (while (and (> (setq n (1- n)) 0) en) (setq en (entnext en)))
 (if (and (setq ed (entget en)) (eq (cdr (assoc 0 ed)) "ATTRIB")) en)
)

Edited by irneb
Modified code for working at last entity

Share this post


Link to post
Share on other sites
BIGAL

Thanks Lee but I am doing something wrong, slowly I am converting to VL the block will have multiple attributes so looking at replacing the attribute value with a string the block can occur multiple times no probs there, get it to work for one for a start.

 

(vlax-invoke (vlax-ename->vla-object e) 'getattributes)) how does this return a list ? In your code your using vla-get-tagstring from the attributes.

 

Any help would be appreciated.

Share this post


Link to post
Share on other sites
irneb

The vlax-invoke generally tries to return a more lisp like value than the vlax-invoke-method or the vla-**** methods. Thus the array returned by the block reference's GetAttributes method is converted to a list through the vlac-invoke. So you end up with a list of ActiveX objects, each pointing to an attribute. So, to get the 4th attribute you'll use the nth function. And to get its TagString:

(vla-get-TagString (nth 3 (vlax-invoke (vlax-ename-vla-object e) 'GetAttributes)))

Though I'd advise to store the list of attribute objects to a variable rather than retrieve them each time you want to work on the list:

(setq attribs (vlax-invoke (vlax-ename-vla-object e) 'GetAttributes))
(vla-get-TagString (nth 3 attribs)) ;Get 4th's TagString
(vla-get-TextString (nth 3 attribs)) ;Get 4th's TextString
(vla-get-TagString (nth 4 attribs)) ;Get 5th's TagString
(vla-get-TextString (nth 4 attribs)) ;Get 5th's TextString

Note though if you use nth with an index of >= the list's length it returns nil - so the vla-get-** functions would cause an error in such cases.

Share this post


Link to post
Share on other sites
BIGAL

Thanks Irneb will have a go tomorrow at get and put the nth x attrib etc this is basicly same method as my VBA code

Share this post


Link to post
Share on other sites
irneb
Thanks Irneb will have a go tomorrow at get and put the nth x attrib etc this is basicly same method as my VBA code
It is very close to the same ideas as in VBA. The major difference is that AutoLisp has no concept of Object Orientation, thus you can't directly call an ActiveX Object's method or work with its properties. For this the vla/vlax functions provide an alternative so it works inside ALisp.

 

Basically there's 3 "official" functions: vlax-invoke-method, vlax-put-property and vlax-get-property. Each of these need the object as its first argument, then the MethodName/PropertyName. Then the vlax-put-property needs the value to store, the vlax-invoke-method "might" need other arguments depending on if the object's method requires them.

 

Then because the ActiveX objects use different data-types than are available inside ALisp, the new variant & safearray types are created. There are several vlax functions to convert, create, modify, etc. on these data types. The undocumented vlax-invoke, vlax-get & vlax-put functions try to automatically convert to-and-from variants & safearrays.

 

Then there are shortcut methods created: the vla-* methods. This is basically done similar to what happens in the vlax-import-type-library function. I.e. a prefix is placed on methods, properties and constants.

 

And due to ActiveX usually using collection type objects, the vlax-for allows you to iterate such collections much like the foreach iterates through normal Lisp-lists.

 

And then when you come to something more advanced is where a method has some ByRef / Out arguments. I.e. where the method modifies an (or more) argument's value. In such case a lisp-"trick" is used. If you pass a variable with a quote prefix it's as if it's passed by reference. Basically it works the other way round than in VBA, where you'd declare your function / sub's arguments as ByRef - you'l quote the variable when calling such function. E.g. when you work with XData the GetXData method sends the types and values in 2 separate arguments as VariantArrays. In such case I'd do something like this:

(vlax-invoke-method Obj 'GetXData "MyXDataAppName" 'XDTypes 'XDValues)
(setq XDTypes (mapcar '(lambda (item) (if (= (type item) 'VARIANT) (vlax-variant-value item) item)) (vlax-safearray->list (vlax-variant-value XDTypes))))
(setq XDValues (mapcar '(lambda (item) (if (= (type item) 'VARIANT)  (vlax-variant-value item) item)) (vlax-safearray->list  (vlax-variant-value XDValues ))))

The last 2 lines is a bit complex for someone coming from VBA, since VBA doesn't have such constructions as mapcar and lambda. But basically what happens here is after converting the variant array into a list of variants, it's run through the "temporary" function made by lambda one at a time. The lambda function here checks if each is a variant, then converts it else just passes it as-is. Then mapcar performs this on each item in the list and passes the modified results as a list. So you end up with the values in lisp-usable types in the 2 lists. It's a lot like what the vlax-invoke function does to try for lisp-like-datatypes, though there it sometimes has problems with these output arguments, so I wouldn't use it in such cases.

Share this post


Link to post
Share on other sites
DVDM

I've been looking for something very similar, and this script from Lee Mac comes the closest to what I want to achieve.

 

Being able to click on the source block, and then the destination block, while specifying different source and destination attributes is exactly what I need. What I can't figure out at this point is how to customise this to copy more than one attribute value with the same operation.

If I could enter a number of different source and destination tags, and copy those values across from the source to the target that would be perfect to quickly update a new title block, with values from an old title block.

 

It would be something like:

Source attribute: "TITLE1" Destination attribute: "NEWTITLE1"

Source attribute: "TITLE2" Destination attribute: "NEWTITLE2"

Source attribute: "TITLE3" Destination attribute: "NEWTITLE3"

Source attribute: "DWGNUM" Destination attribute: "DOCUMENT-NUMBER"

 

The reason I need the ability to select the source and destination block is that I can have multiple blocks of both source and destination within a single dwg file, and they need to have different values (so I can't simply update by block name). Each Layout in the drawing will have only one source, and one destination block.

The source block can have different names ("A4TITLE" "A3TITLE" "A2TITLE" etc.), and you can't tell by looking at the block what block name it has.

The destination block will have the same name each time ("NEWTITLE")

 

I have found some code in this discussion thread that sets out to achieve the updating of multiple attributes in a defineable oldtag > newtag mapping:

http://www.cadtutor.net/forum/showthread.php?73583-Copying-Attribute-data-from-one-tittle-block-to-another

but I don't know how to combine that with this lisp from Lee-Mac that allows to visually pick the source and destination blocks.

 

I also found a lisp that allows to pick a source and destination block and will copy the attribute values across, and that works perfect so long as the attributes have the same name (which in my case they don't):

http://forums.augi.com/showthread.php?94955-Copying-attribute-values-among-blocks

But again there I don't know how to modify that with an oldtag>newtag mapping.

 

Does anyone know how to string these routines together in a way that makes that work? I have attached a drawing with the attributed blocks and attribute names described.

Many thanks!

OldToNewAttributes.dwg

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×