Autogenerate Attribute callout with LISP

Autogenerate Attribute callout with LISP

Kyle.para
Advocate Advocate
3,974 Views
33 Replies
Message 1 of 34

Autogenerate Attribute callout with LISP

Kyle.para
Advocate
Advocate

Hi

 

I have a bunch of callouts setup as attributes inside of blocks.

I need to auto generate numbers usually 1 - 50 from either left to right or right to left depending on the drawing.

 

I also need a separate LISP that will auto generate letters in the same fashion but also on the same drawing.

 

Will I have to pick all the blocks or can it be auto generated some how?

If it can I'm guessing I would have to distinguish between the attributes I need numbered and the attributes I need lettered some how?

 

Thanks,

0 Likes
Accepted solutions (4)
3,975 Views
33 Replies
Replies (33)
Message 2 of 34

john.uhden
Mentor
Mentor

Yes, it can be done automatically if you can specify the block name and/or use attribute tags that are specifically recognizable, such as "TAG#" vs. "TAG$" or maybe better "#TAG" and "$TAG."  The $ and # characters date back to my ancient Basic programming days which differentiated string variables vs. numeric variables.

I am presuming that you may use different blocks with the same type of attributes so that each block would be numbered sequentially but separately from a different block.  And, yes, we could sort the attributes by their X coordinate so they are labeled sequentially from left to right.

John F. Uhden

0 Likes
Message 3 of 34

Kyle.para
Advocate
Advocate

The way that you described it by differentiating it with the # $ is exactly how I envision it working.

Would I need two different lisp commands to get the the data entered from left to right and then form right to left if required? Or could I have an input to pick the right way when the program starts?

 

Also if anyone has anything close to a starting point I would greatly appreciate it.  I am new to this in CAD and just learned how to load an application, let alone program one haha.

0 Likes
Message 4 of 34

john.uhden
Mentor
Mentor

Good news about the naming convention.

Yes, we would give the user a choice e.g.

Direction <Left to right>/Right to left: 

 

where the user can hit "L" or just enter for Left, and "R" for right.

 

Are the attributes always renumbered starting from 1 or A, or do you want to maintain certain existing numbers?

You said from 1 to 50.  Since there are only 26 letters in the alphabet, do you want to continue with "AA" "AB" etc?

And might you want to go higher than 52?

Also, will there be any confusion using the letter "O" as it looks like a zero?

All capitals, right?

 

Right now I would automatically include all inserts that have attributes, but then exclude any attributes whose tags don't match the naming convention (which will be hard-wired into the code).  That way the only user input required is for the direction.

John F. Uhden

0 Likes
Message 5 of 34

john.uhden
Mentor
Mentor

I lied.  The user also has to pick "Alpha" or "Numeric."

John F. Uhden

0 Likes
Message 6 of 34

Kyle.para
Advocate
Advocate

where the user can hit "L" or just enter for Left, and "R" for right. AWESOME

 

Are the attributes always renumbered starting from 1 or A, or do you want to maintain certain existing numbers? Yes always start form 1 & A

You said from 1 to 50.  Since there are only 26 letters in the alphabet, do you want to continue with "AA" "AB" etc? That would be great.

And might you want to go higher than 52? It could be a possibility, but not likely.

Also, will there be any confusion using the letter "O" as it looks like a zero? No that won't be a problem.

All capitals, right? Yes

 

Right now I would automatically include all inserts that have attributes, but then exclude any attributes whose tags don't match the naming convention (which will be hard-wired into the code).  That way the only user input required is for the direction. It would be great if it would enter the whole thing as you described all at once and only require the user input for direction, but if it's not possible that is OK as well.  I guess we just have to run it twice?

 

Thanks a lot John.

0 Likes
Message 7 of 34

john.uhden
Mentor
Mentor
Yes. There is no need for the user to select Numeric vs. Alpha since the
attribute tag makes the distinction.

John F. Uhden

0 Likes
Message 8 of 34

john.uhden
Mentor
Mentor
Accepted solution

Here ya go...

 

;; AutoAtt.lsp for Kyle.Para by John Uhden (2-11-17)
;; Program labels attributes both numerically and alphabetically (like Excel columns)
;; sorted from either Left to right or Right to left based on the WCS.
;; Attribute tags ending in "#" are labeled numerically,
;; Attribute tags ending in "$" are labeled alphabetically.
;; Many many thanks to Doug Broad for contributing the ntos function which translates
;; integers to Excel-like strings.
;; Labels start at 1 ("A") and can continue into the hundrdes of thousands.
;;
(defun c:AutoAtt ( / *error* vars Dir <> Atts n# n$ #Atts $Atts)
  (vl-load-com)
  (defun *error* (err)
    (mapcar '(lambda (x)(setvar (car x)(cdr x))) vars)
    (vla-endundomark *doc*)
    (cond
      ((not err))
      ((wcmatch (strcase err) "*CANCEL*,*QUIT*"))
      (1  (princ (strcat "\nERROR: " err)))
    )
    (princ)
  )
  (or *acad* (setq *acad* (vlax-get-acad-object)))
  (or *doc* (setq *doc* (vla-get-ActiveDocument *acad*)))
  (vla-endundomark *doc*)
  (vla-startundomark *doc*)
  (setq vars (mapcar '(lambda (x)(cons x (getvar x))) '("cmdecho")))
  (mapcar '(lambda (x)(setvar (car x) 0))  vars)
  (command "_.expert" (getvar "expert")) ;; dummy command
  (setq n# 0 n$ 0)
  ;;===================================================================
  ;; By Doug Broad (2-10-17)
    ;; Sorry @john.uhden.  Been busy with a Statics and Strengths class today.
    ;; Here is one that works with a base 25, using 0 as A and 25 as Z.
    ;; Convert base 10 to letters
    ;; 0 = a to 25 = z for each digit.
    ;; Works for 0 to (- (expt 26 7) 1) integer maximum
    ;; JFU did Cooperial compaction and adjusted n by one
    (defun ntos (n)
      (if (< (setq n (1- n)) 26)
        (chr (+ 65 n))
        (strcat (ntos (/ n 26))(chr (+ 65 (rem n 26))))
      )
  )
  (initget "Left Right")
  (setq Dir (getkword "\nSort from <Left to right>/Right to left: "))
  (if (not Dir)(setq Dir "Left"))
  (setq <> (if (= Dir "Left") < >))
  (if (ssget "x" '((0 . "INSERT")(66 . 1)))
    (vlax-for Object (vla-get-ActiveSelectionSet *doc*)
      (and
        (= (vlax-get Object 'HasAttributes) -1)
        (foreach Att
            (vlax-invoke Object 'GetAttributes)
            (setq Atts (cons Att Atts))
        )
      )
    )
    (prompt "\nNo blocks with attributes in drawing")
  )
  (foreach Att Atts
    (cond
      ((wcmatch (vlax-get Att 'Tagstring) "*`#")
        (setq #Atts (cons Att #Atts))
      )
      ((wcmatch (vlax-get Att 'Tagstring) "*$")
        (setq $Atts (cons Att $Atts))
      )
    )
  )
  (princ (strcat "\nLength of #Atts = " (itoa (length #atts))))
  (princ (strcat "\nLength of $Atts = " (itoa (length $atts))))
  (setq #Atts (vl-sort #Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (setq $Atts (vl-sort $Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (foreach Att #Atts (vlax-put Att 'Textstring (itoa (setq n# (1+ n#)))) (vla-update Att))
  (foreach Att $Atts (vlax-put Att 'Textstring (ntos (setq n$ (1+ n$)))) (vla-update Att))
  (*error* nil)
)
(defun c:AA ()(c:AutoAtt))

John F. Uhden

Message 9 of 34

ВeekeeCZ
Consultant
Consultant

If you add square brackets, then, since version 2013, are the options nicely clickable! Square brackets, delimited by a slash [Apple/Banana/Cactus]

 

(getkword "\nSort from [<Left to right>/Right to left]: ")

 

See HERE, 1'14

Message 10 of 34

john.uhden
Mentor
Mentor

I'm still 11 years behind that, but that's neat to know.  Thanks!

John F. Uhden

0 Likes
Message 11 of 34

Kyle.para
Advocate
Advocate

@john.uhden

 

Thank you so much for your help this is awesome!  You are a rock star my friend.

 

@ВeekeeCZ

 

Thanks, I added that into the program and it works great.

0 Likes
Message 12 of 34

john.uhden
Mentor
Mentor

Could you please mark my previous response as accepted?  It helps to clean up the open topics.

John F. Uhden

0 Likes
Message 13 of 34

Kyle.para
Advocate
Advocate

Hey John,

 

I did already? Is that not what you're seeing?

0 Likes
Message 14 of 34

john.uhden
Mentor
Mentor

Thank you, Kyle.  I just had not noticed.

John F. Uhden

0 Likes
Message 15 of 34

Kyle.para
Advocate
Advocate

Hey John,

 

I have three problems when using this and was wondering how hard it would be to fix.

 

1. Is there anyway that we can reserve number 1 & 2 and increment the numbers starting from 3?

2. For the numbers 3 - 9 can we add a 0 in front of the numbers? 03, 04, etc...

 

3. Can we add a 3rd routine that will work like the $, but will use a % sign instead and increment in letters the same way? I started this one but, I don't think it was as simple as I thought it would be. There's something I am missing.  I have attached what I did so far.

 

Thanks,

 

 

 

;; AutoAtt.lsp for Kyle.Para by John Uhden (2-11-17)
;; Program labels attributes both numerically and alphabetically (like Excel columns)
;; sorted from either Left to right or Right to left based on the WCS.
;; Attribute tags ending in "#" are labeled numerically,
;; Attribute tags ending in "$" are labeled alphabetically.
;; Attribute tags ending in "%" are labeled alphabetically.
;; Many many thanks to Doug Broad for contributing the ntos function which translates
;; integers to Excel-like strings.
;; Labels start at 1 ("A") and can continue into the hundrdes of thousands.
;;
(defun c:AutoAtt ( / *error* vars Dir <> Atts n# n$ n% #Atts $Atts %Atts)
  (vl-load-com)
  (defun *error* (err)
    (mapcar '(lambda (x)(setvar (car x)(cdr x))) vars)
    (vla-endundomark *doc*)
    (cond
      ((not err))
      ((wcmatch (strcase err) "*CANCEL*,*QUIT*"))
      (1  (princ (strcat "\nERROR: " err)))
    )
    (princ)
  )
  (or *acad* (setq *acad* (vlax-get-acad-object)))
  (or *doc* (setq *doc* (vla-get-ActiveDocument *acad*)))
  (vla-endundomark *doc*)
  (vla-startundomark *doc*)
  (setq vars (mapcar '(lambda (x)(cons x (getvar x))) '("cmdecho")))
  (mapcar '(lambda (x)(setvar (car x) 0))  vars)
  (command "_.expert" (getvar "expert")) ;; dummy command
  (setq n# 0 n$ 0 n% 0)
  ;;===================================================================
  ;; By Doug Broad (2-10-17)
    ;; Sorry @john.uhden.  Been busy with a Statics and Strengths class today.
    ;; Here is one that works with a base 25, using 0 as A and 25 as Z.
    ;; Convert base 10 to letters
    ;; 0 = a to 25 = z for each digit.
    ;; Works for 0 to (- (expt 26 7) 1) integer maximum
    ;; JFU did Cooperial compaction and adjusted n by one
    (defun ntos (n)
      (if (< (setq n (1- n)) 26)
        (chr (+ 65 n))
        (strcat (ntos (/ n 26))(chr (+ 65 (rem n 26))))
      )
  )
  (initget "Left Right")
  (setq Dir (getkword "\nSort from [<Left to right>/Right to left]: "))
  (if (not Dir)(setq Dir "Left"))
  (setq <> (if (= Dir "Left") < >))
  (if (ssget "x" '((0 . "INSERT")(66 . 1)))
    (vlax-for Object (vla-get-ActiveSelectionSet *doc*)
      (and
        (= (vlax-get Object 'HasAttributes) -1)
        (foreach Att
            (vlax-invoke Object 'GetAttributes)
            (setq Atts (cons Att Atts))
        )
      )
    )
    (prompt "\nNo blocks with attributes in drawing")
  )
  (foreach Att Atts
    (cond
      ((wcmatch (vlax-get Att 'Tagstring) "*`#")
        (setq #Atts (cons Att #Atts))
      )
      ((wcmatch (vlax-get Att 'Tagstring) "*$")
        (setq $Atts (cons Att $Atts))
      )
      ((wcmatch (vlax-get Att 'Tagstring) "*%")
        (setq %Atts (cons Att %Atts))
      ) 
    )
  )
  (princ (strcat "\nLength of #Atts = " (itoa (length #atts))))
  (princ (strcat "\nLength of $Atts = " (itoa (length $atts))))
  (princ (strcat "\nLength of %Atts = " (itoa (length %atts))))
  (setq #Atts (vl-sort #Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (setq $Atts (vl-sort $Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (setq %Atts (vl-sort %Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (foreach Att #Atts (vlax-put Att 'Textstring (itoa (setq n# (1+ n#)))) (vla-update Att))
  (foreach Att $Atts (vlax-put Att 'Textstring (ntos (setq n$ (1+ n$)))) (vla-update Att))
  (foreach Att $Atts (vlax-put Att 'Textstring (ntos (setq n% (1+ n%)))) (vla-update Att))
  (*error* nil)
)
(defun c:AA ()(c:AutoAtt))
0 Likes
Message 16 of 34

john.uhden
Mentor
Mentor

You had it almost perfect!  Just forgot to set n% to an integer.

I'm not sure if you wanted to start the alpha numbering from "C."

If so, change the blue line below so that n$ and n% start at 2.

I did NOT test.

 

;; AutoAtt.lsp for Kyle.Para by John Uhden (2-11-17), rev. (02-24-17)
;; Program labels attributes both numerically and alphabetically (like Excel columns)
;; sorted from either Left to right or Right to left based on the WCS.
;; Attribute tags ending in "#" are labeled numerically,
;; Attribute tags ending in "$" are labeled alphabetically.
;; Attribute tags ending in "%" are labeled alphabetically.
;; Many many thanks to Doug Broad for contributing the ntos function which translates
;; integers to Excel-like strings.
;; Labels start at 1 ("A") and can continue into the hundrdes of thousands.
;;
(defun c:AutoAtt ( / *error* vars <> Atts n# n$ n% #Atts $Atts %Atts ntos itos)
  (princ "\nAutoAtt v1.1 (02-24-17)")
  (vl-load-com)
  (defun *error* (err)
    (mapcar '(lambda (x)(setvar (car x)(cdr x))) vars)
    (vla-endundomark *doc*)
    (cond
      ((not err))
      ((wcmatch (strcase err) "*CANCEL*,*QUIT*"))
      (1  (princ (strcat "\nERROR: " err)))
    )
    (princ)
  )
  (or *acad* (setq *acad* (vlax-get-acad-object)))  ;; I like to leave global
  (or *doc* (setq *doc* (vla-get-ActiveDocument *acad*))) ;; I like to leave global
  (vla-endundomark *doc*)
  (vla-startundomark *doc*)
  (setq vars (mapcar '(lambda (x)(cons x (getvar x))) '("cmdecho")))
  (mapcar '(lambda (x)(setvar (car x) 0))  vars)
  (command "_.expert" (getvar "expert")) ;; dummy command
  (setq n# 2 n$ 0 n% 0) ;; Kyle wants to reserve values "1" and "2" (02-24-17)
  ;;===================================================================
  ;; By Doug Broad (2-10-17)
    ;; Sorry @john.uhden.  Been busy with a Statics and Strengths class today.
    ;; Here is one that works with a base 25, using 0 as A and 25 as Z.
    ;; Convert base 10 to letters
    ;; 0 = a to 25 = z for each digit.
    ;; Works for 0 to (- (expt 26 7) 1) integer maximum
    ;; JFU did Cooperian compaction and adjusted n by one
    (defun ntos (n)
      (if (< (setq n (1- n)) 26)
        (chr (+ 65 n))
        (strcat (ntos (/ n 26))(chr (+ 65 (rem n 26))))
      )
  )
  ;; Added itos (02-24-17) so that numbers from 3 to 9
  ;; are preceded by a "0", as in "05"
  (defun itos (n)
    (if (>= 3 n 9)(strcat "0" (itoa n))(itoa n))
  )
  (initget "Left Right")
  (setq <> (getkword "\nSort from [<Left to right>/Right to left]: "))
  (if (or (not <>)(= <> "Left"))
    (setq <> <)
    (setq <> >)
  )
  (if (ssget "x" '((0 . "INSERT")(66 . 1)))
    (vlax-for Object (vla-get-ActiveSelectionSet *doc*)
      (and
        (= (vlax-get Object 'HasAttributes) -1)
        (foreach Att
            (vlax-invoke Object 'GetAttributes)
            (setq Atts (cons Att Atts))
        )
      )
    )
    (prompt "\nNo blocks with attributes in drawing")
  )
  (foreach Att Atts
    (cond
      ((wcmatch (vlax-get Att 'Tagstring) "*`#")
        (setq #Atts (cons Att #Atts))
      )
      ((wcmatch (vlax-get Att 'Tagstring) "*$")
        (setq $Atts (cons Att $Atts))
      )
      ((wcmatch (vlax-get Att 'Tagstring) "*%")
        (setq %Atts (cons Att %Atts))
      ) 
    )
  )
  (princ (strcat "\nLength of #Atts = " (itoa (length #atts))))
  (princ (strcat "\nLength of $Atts = " (itoa (length $atts))))
  (princ (strcat "\nLength of %Atts = " (itoa (length %atts))))
  (setq #Atts (vl-sort #Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (setq $Atts (vl-sort $Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (setq %Atts (vl-sort %Atts '(lambda (a b)(<> (car (vlax-get a 'InsertionPoint))(car (vlax-get b 'InsertionPoint))))))
  (foreach Att #Atts (vlax-put Att 'Textstring (itos (setq n# (1+ n#)))) (vla-update Att))
  (foreach Att $Atts (vlax-put Att 'Textstring (ntos (setq n$ (1+ n$)))) (vla-update Att))
  (foreach Att $Atts (vlax-put Att 'Textstring (ntos (setq n% (1+ n%)))) (vla-update Att))
  (*error* nil)
)
(defun c:AA ()(c:AutoAtt))

John F. Uhden

0 Likes
Message 17 of 34

john.uhden
Mentor
Mentor

Oops.  I didn't ask if "1" and "2" might already be present and if so whether to leave them alone.  If so, the code needs fixing.

John F. Uhden

0 Likes
Message 18 of 34

Kyle.para
Advocate
Advocate

Yeah 1 and 2 are already reserved for something so I need it to start at #03.

 

I also need it to increment 3-9 as 03-09.

 

The letters are OK to start at A.

0 Likes
Message 19 of 34

john.uhden
Mentor
Mentor

Not enough information.

 

The way the program is now, it will grab ALL the attributes and start numbering them from whatever start number you want, no matter what their current value is.

 

So which ones are "reserved" as "1" and "2", any of the existing?  If so then we have to check existing values before assigning new values.

 

If 1 and 2 are to be constant, then maybe they should have a tag ending in other than #, or $, or %, so that they are left alone?

 

Or do you mean that the first two #s get numbered "1" and "2" with the remainder from 3 - 9 being "03" through "09?"

 

Or will you be setting "1" and "2" as constant properties?  If so we need to check for that.  But I think that's a property of the attribute definition, not the attribute of the insertion.

 

I'm sorry if I sound a little tired and cranky, but I just attended a tedious dinner at the house of someone I really dislike, so I am cranky.  Hopefully my scotch tones me down a bit.

John F. Uhden

0 Likes
Message 20 of 34

Kyle.para
Advocate
Advocate
Lol. John you are fantastic dude. Are you still looking for work?. What
kind of scotch do you drink because I owe you a bottle by now.

I just want the programme to assume that #01 & #02 are taken. Then I want
it to carry on from 03 to 04.... and so on.

I have two pieces of equipment that I would like to remain constant. That
being #01 & #02. Sorry I didn't know this until I tried the routine. Trial
and error i guess, but #02 has a lot of stuff I need to add to it 02a, 02b
etc.

I also didn't know that I needed the attribute from 1-9 to be 01-09,
because I am pulling the data from an attribute extraction. Then I sort the
item # based on the tag. This then gives me a schedule based on all of the
tags. Therefore att 2 was ending after 19 etc. Hopefully this makes more
sense this time.

I recently realised that I assume everyone understands me, but I have
failed to make sure that they see things through the same vision.

I hope this is more clear and I appreciate your help.
0 Likes