Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Edit Attributes of a Block With LISP

13 REPLIES 13
SOLVED
Reply
Message 1 of 14
TomTom111
30812 Views, 13 Replies

Edit Attributes of a Block With LISP

Hello,

I think I have covered this before, but I cant seem to get the hang of it, so I am again seeking assistance.

 

I have a block named "WD_MLRH".

There are 3 specific attributes which I want to change in it.

The attribute names are...

RUNGCNT, RUNGDIST and RUNGFIRST.

RUNGCNT will = 20

RUNGDIST will = 1.0 (or 1 will suffice)

 

RUNGFIRST will be decided by a user input.

 

Something along the lines of...

(setq RUNGFIRST (getint "Enter first rung number: "))

I'm sure this is completly wrong, but i hope that helps portray what I'm trying to accomplish.

 

Thanks in advanced!

 

Thomas Walls

CADMASTER TECHNOLOGIES, LLC

www.cadmastertech.com

13 REPLIES 13
Message 2 of 14
balisteor
in reply to: TomTom111

here is a good routine to change attributes on blocks:

(defun reptag (tag newvalue ent / alist )
(if (and (= (type ent) (read "VLA-OBJECT")) newvalue)
(progn
(setq alist ( vlax-invoke ent 'GetAttributes))
 (foreach a alist
  (if (=  (vla-get-tagstring a) tag)
  (vlax-put-property a 'TextString newvalue)
  );i
 );fe
);p
(if (= 'ename (type ent)) (reptag tag newvalue (vlax-ename->vla-object ent)));i
);i
(princ));d

 

 

 

 

To use the above routine with what your working on:

(defun c:WD_MLRHchange ( / ent)
(setq ent (car (entsel)))
(reptag "RUNGCNT" "20" ent)
(reptag "RUNGDIST" "1.0" ent)
(reptag "RUNGFIRST" (itoa (getint "Enter first rung number: ")))
(princ))

 

 

 

 

Message 3 of 14
Lee_Mac
in reply to: TomTom111

You might find my set of Attribute Functions useful for this task.

 

Lee

Message 4 of 14
TomTom111
in reply to: balisteor

Thanks for the reply balisteor!

 

Your routine works great.

Is there a way to automate it to edit a attributes in a block that are already in the drawing?

Same attribute names, same block name, and same attribute values with a twist to RUNGFIRST.

 

I was thinking something like...

 

(setq x (getint "Enter first rung number: "))

(reptag "RUNGFIRST" x ent)

 

Hope I'm showing that correctly...

 

Thanks!

 

Thomas Walls

CADMASTER TECHNOLOGIES, LLC

www.cadmastertech.com

Message 5 of 14
TomTom111
in reply to: Lee_Mac

Thanks Lee_Mac!

 

I am familiar with your website, and have used several of your programs from there before.

Not sure why I didn't think to look there as well...

 

I will give some of these a try, and see if they're what im looking for.

 

Thanks!

 

Thomas Walls

CADMASTER TECHNOLOGIES, LLC

www.cadmastertech.com

Message 6 of 14
Lee_Mac
in reply to: TomTom111

You're welcome!

 

Please be aware that the set of attribute functions to which I have referred are not complete programs, but functions to be called from other programs with the required parameters as noted in the comments.


Ask if you need help using the functions in your program.

 

Lee

Message 7 of 14
TomTom111
in reply to: Lee_Mac

Thanks,

 

I think this is the appropriate code for the routine I am trying to make.

;; Set Attribute Value  -  Lee Mac
;; Sets the value of the first attribute with the given tag found within
;; the supplied block, if present.
;; Arguments:
;; blk - [vla] VLA Block Reference Object
;; tag - [str] Attribute TagString
;; val - [str] Attribute Value
;; Returns: [str] Attribute value if successful, else nil.

(defun LM:vl-SetAttributeValue ( blk tag val )
    (setq tag (strcase tag))
    (vl-some
        (function
            (lambda ( att )
                (if (= tag (strcase (vla-get-tagstring att)))
                    (progn
                        (vla-put-textstring att val)
                        val
                    )
                )
            )
        )
        (vlax-invoke block 'getattributes)
    )
)

Now I want it to find 3 tags in a block named WD_MLRH...

RUNGCNT, RUNGDIST and RUNGFIRST

Without asking the user to select the block that has these attributes, I want it to change the values in the block.

RUNGCNT = 20

RUNGDIST = 1

and

RUNGFIRST = an integer value I have stored as x.

Will I have to have 3 separate arguments for each tag?

Ex. tag1 tag2 tag3

 

Sorry for the many questions, I am still very new to the LISP language

 

Message 8 of 14
Lee_Mac
in reply to: TomTom111

No need to apologise, I welcome the questions and appreciate that you are trying to learn.

 

For this task, I would recommend utilising my Vanilla AutoLISP Set Attribute Values function (either recursive or iterative, it doesn't matter for this task). This function requires an attributed block entity name argument and an association list of tags & values to be set in the supplied block.

 

Below I have put together a fully-commented example program to demonstrate how you might call this function to operate on your blocks:

 

(defun c:attchange ( / i l s x )
    ;; Define function, declare local variable symbols.
    ;; To understand why variable declaration is important, see http://bit.ly/15Qw104
    
    (if ;; If we can retrieve a set of attributed blocks
        (setq s ;; Assign the selection set pointer to variable 's'
            (ssget "_X" ;; Search entire database (see http://bit.ly/137NmOJ for more info)
               '(
                    (0 . "INSERT") ;; Block References
                    (66 . 1) ;; Attributed
                    (2 . "WD_MLRH") ;; with name WD_MLRH (this assumes block is non-dynamic)
                )
            ) ;; end SSGET
        ) ;; end SETQ
        (progn
            ;; Evaluate the following expressions and return the result
            ;; of the last expression evaluated. PROGN is used as a
            ;; wrapper function so that we can pass multiple expressions
            ;; to the IF function as a single argument constituting the
            ;; 'then' parameter.
            
            (initget 6) ;; (+ 2 4): 2=prevent zero, 4=prevent negative
            (if (setq x (getint "\nEnter first rung number: ")) ;; Prompt user for rung number > 0
                (progn
                    ;; See above explanation for PROGN

                    ;; Construct an association list of attribute tags & values
                    ;; to pass to the LM:setattributevalues function
                    (setq l
                        (cons
                            (cons "RUNGFIRST" (itoa x)) ;; => e.g. ("RUNGFIRST" . "2")

                            ;; Use a quoted literal list for the remaining tags/values
                            ;; as there are no expressions to be evaluated.
                            ;; For an explanation of the apostrophe, see http://bit.ly/1bW3rQK
                           '(
                                ("RUNGCNT"  . "20")
                                ("RUNGDIST" .  "1")
                            )
                        ) ;; end CONS
                    ) ;; end SETQ
                    
                    ;; The resulting list might look like this:
                    ;; l = (("RUNGFIRST" . "2") ("RUNGCNT"  . "20") ("RUNGDIST" .  "1"))
                
                    ;; Repeat the following expressions a number of times
                    ;; equal to the number of items in the selection set.
                    ;; Note that the numerical argument for the repeat function
                    ;; is only evaluated once and hence the integer index variable 'i'
                    ;; will only be assigned a value once.
                    (repeat (setq i (sslength s))
;; Call the LM:setattributevalues function with the block entity name ;; and association list of attribute tags/values to be set. (LM:SetAttributeValues (ssname s (setq i (1- i))) l) ) ;; end REPEAT ) ;; end PROGN ) ;; end IF ) ;; end PROGN ;; Else no blocks were found... (princ "\nNo \"WD_MLRH\" blocks were found.") ) ;; end IF (princ) ;; Supress the return of the last evaluated expression ) ;; end DEFUN ;; Set Attribute Values - Lee Mac ;; Sets the block attributes whose tags are found in the supplied ;; assocation list to their associated values. ;; Arguments: ;; blk - [ent] Block (Insert) Entity Name ;; lst - [lst] Association list of ((<TAG> . <Value>) ... ) ;; Returns: nil (defun LM:SetAttributeValues ( blk lst / enx itm ) (if (= "ATTRIB" (cdr (assoc 0 (setq enx (entget (setq blk (entnext blk))))))) (if (setq itm (assoc (strcase (cdr (assoc 2 enx))) lst)) (progn (if (entmod (subst (cons 1 (cdr itm)) (assoc 1 enx) enx)) (entupd blk) ) (LM:SetAttributeValues blk lst) ) (LM:SetAttributeValues blk lst) ) ) ) (princ) ;; Suppress the return of any loading expressions

 

Do not hesitate to ask if you have any questions about the code.

 

Lee

Message 9 of 14
TomTom111
in reply to: Lee_Mac

Lee_Mac

 

This is an amazing explanation, thanks!

The routine works great as well.

 

I am, however, running into an issue with a maximum integer value, where the value for RUNGFIRST must be between 1 and 32767. Is there a way to increase this number to  50000 or more?

 

Thanks again!

 

Thomas Walls

CADMASTER TECHNOLOGIES, LLC

www.cadmastertech.com

 

Message 10 of 14
Lee_Mac
in reply to: TomTom111


@TomTom111 wrote:

Lee_Mac

 

This is an amazing explanation, thanks!

The routine works great as well.


 

Thank you Tom, I'm glad my explanations were comprehensible! Smiley Happy

 


@TomTom111 wrote:

I am, however, running into an issue with a maximum integer value, where the value for RUNGFIRST must be between 1 and 32767. Is there a way to increase this number to  50000 or more?


 

This is a remnant from the early days of AutoLISP: the getint function assumes that integers are stored as 16-bit signed integers, and hence limited to a range of -32,768 to 32,767 (under two's complement).

 

Compare that to the 32-bit & 64-bit signed integers which we use nowadays, which have respective ranges of -2,147,483,648 to 2,147,483,647 (32-bit) and -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (64-bit)...

To circumvent the issue, prompt the user with the getreal function and then use the fix function to convert the real (double) to an integer.

Message 11 of 14
TomTom111
in reply to: Lee_Mac

Lee_Mac,

 

I made the change to

 

(setq x (getreal "Enter first rung number: ")) ;; Prompt user for rung number > 0 
			(fix x)

 And when I run the code to change the attributes in the block, it doens't seem to want it!

It won't change any of the attribute values in the block.

But when i change it back to getint, it will work fine, with the maximum back to 32767.

 

Thanks

 

Thomas Walls

CADMASTER TECHNOLOGIES, LLC

www.cadmastertech.com

 

Message 12 of 14
Lee_Mac
in reply to: TomTom111

Close! But the issue is because you did not assign the new value to the variable 'x' after converting the double (real) value to an integer.

 

Consider the following code:

 

(defun c:attchange ( / i l s x )
    ;; Define function, declare local variable symbols.
    ;; To understand why variable declaration is important, see http://bit.ly/15Qw104
    
    (if ;; If we can retrieve a set of attributed blocks
        (setq s ;; Assign the selection set pointer to variable 's'
            (ssget "_X" ;; Search entire database (see http://bit.ly/137NmOJ for more info)
               '(
                    (0 . "INSERT") ;; Block References
                    (66 . 1) ;; Attributed
                    (2 . "WD_MLRH") ;; with name WD_MLRH (this assumes block is non-dynamic)
                )
            ) ;; end SSGET
        ) ;; end SETQ
        (progn
            ;; Evaluate the following expressions and return the result
            ;; of the last expression evaluated. PROGN is used as a
            ;; wrapper function so that we can pass multiple expressions
            ;; to the IF function as a single argument constituting the
            ;; 'then' parameter.
            
            (initget 6) ;; (+ 2 4): 2=prevent zero, 4=prevent negative
            (if (setq x (getreal "\nEnter first rung number: ")) ;; Prompt user for rung number > 0
                (progn
                    ;; See above explanation for PROGN

                    ;; Construct an association list of attribute tags & values
                    ;; to pass to the LM:setattributevalues function
                    (setq l
                        (cons
                            (cons "RUNGFIRST" (itoa (fix x))) ;; => e.g. ("RUNGFIRST" . "2")

                            ;; Use a quoted literal list for the remaining tags/values
                            ;; as there are no expressions to be evaluated.
                            ;; For an explanation of the apostrophe, see http://bit.ly/1bW3rQK
                           '(
                                ("RUNGCNT"  . "20")
                                ("RUNGDIST" .  "1")
                            )
                        ) ;; end CONS
                    ) ;; end SETQ
                    
                    ;; The resulting list might look like this:
                    ;; l = (("RUNGFIRST" . "2") ("RUNGCNT"  . "20") ("RUNGDIST" .  "1"))
                
                    ;; Repeat the following expressions a number of times
                    ;; equal to the number of items in the selection set.
                    ;; Note that the numerical argument for the repeat function
                    ;; is only evaluated once and hence the integer index variable 'i'
                    ;; will only be assigned a value once.
                    (repeat (setq i (sslength s))

                        ;; Call the LM:setattributevalues function with the block entity name
                        ;; and association list of attribute tags/values to be set.
                        (LM:SetAttributeValues (ssname s (setq i (1- i))) l)
                    ) ;; end REPEAT
                ) ;; end PROGN
            ) ;; end IF
        ) ;; end PROGN

        ;; Else no blocks were found...
        (princ "\nNo \"WD_MLRH\" blocks were found.")
    ) ;; end IF
    
    (princ) ;; Supress the return of the last evaluated expression
) ;; end DEFUN


;; Set Attribute Values  -  Lee Mac
;; Sets the block attributes whose tags are found in the supplied
;; assocation list to their associated values.
;; Arguments:
;; blk - [ent] Block (Insert) Entity Name
;; lst - [lst] Association list of ((<TAG> . <Value>) ... )
;; Returns: nil

(defun LM:SetAttributeValues ( blk lst / enx itm )
    (if (= "ATTRIB" (cdr (assoc 0 (setq enx (entget (setq blk (entnext blk)))))))
        (if (setq itm (assoc (strcase (cdr (assoc 2 enx))) lst))
            (progn
                (if (entmod (subst (cons 1 (cdr itm)) (assoc 1 enx) enx))
                    (entupd blk)
                )
                (LM:SetAttributeValues blk lst)
            )
            (LM:SetAttributeValues blk lst)
        )
    )
)

(princ) ;; Suppress the return of any loading expressions

 

Message 13 of 14
TomTom111
in reply to: Lee_Mac

Lee_Mac

 

Got it working like a charm thanks to you, thanks a million!

 

Thomas Walls

CADMASTER TECHNOLOGIES, LLC

www.cadmastertech.com

Message 14 of 14
Lee_Mac
in reply to: TomTom111

You're very welcome Thomas, I hope you can learn from the code Smiley Happy

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost