Attribute count with table

Attribute count with table

C.Utzinger
Collaborator Collaborator
6,241 Views
51 Replies
Message 1 of 52

Attribute count with table

C.Utzinger
Collaborator
Collaborator

Hello

 

I have found the following code.

 

(defun C:COUNTATT(/ acapp acol acsp adoc atable attdata attitem atts blkdata blkname blkobj col
        column colwidth  datalist en headers pt row sset swap  tabledata tags total txtheight widths x)
  
  ;private function
 
  (defun sum-and-groupby-three (lst / groups res sum tmp)
  (while lst
    (setq tmp        (car lst)
      sum
            (apply '+
               (mapcar 'atoi (mapcar 'cdadr 
                   (setq res (vl-remove-if-not

                           '(lambda (a) (and
                                  (eq (cdr (nth 0 a)) (cdr (nth 0 tmp)))
                                  (eq (cdr (nth 1 a)) (cdr (nth 1 tmp)))
                                  (eq (cdr (nth 2 a)) (cdr (nth 2 tmp)))))

                           lst

                         )
                   )
               ))
            )
      groups    (cons (subst (cons "QTY" (itoa sum))(cadr tmp) tmp) groups)
      lst
            (vl-remove-if
              '(lambda (a) (member a res))

              lst

            )
    )
  )
 
 (reverse groups)
)

;            main part            ;
  (if (setq sset (ssget (list (cons 0 "INSERT") (cons 66 1))))
    (progn
      (setq tabledata nil
        attdata nil
        attitem nil
      )
      
      (while (setq en (ssname sset 0))
    (setq blkobj  (vlax-ename->vla-object en)
          blkname (vla-get-effectivename blkobj)
    )
    (setq atts (vlax-invoke blkobj 'getattributes))
    (foreach attobj    atts

          (setq attitem (cons (vla-get-tagstring attobj) (vla-get-textstring attobj)))
          (setq attdata (cons attitem attdata))

    )

    (setq tabledata (cons (reverse attdata) tabledata))
    (setq attdata nil
          attitem nil
    )
    (ssdel en sset)
      )
(setq headers (mapcar 'car (car tabledata))
        tags    headers 
      )
(setq tabledata (sum-and-groupby-three tabledata))

(setq tabledata (mapcar '(lambda (x)
                 (mapcar 'cdr x)
                   )
                  tabledata
              )
      )


      ;; sort by "A1" :
      
     (setq tabledata (vl-sort   tabledata '(lambda(a b)(< (car a)(car b)))))

 
      (setq total 0)
      (foreach i datalist (setq total (+ total (cdr i))))
    (initget 6)
  (setq txtheight (getreal "\nSpecify Text height for the table <50>:"))
  (cond ((not txtheight)(setq txtheight 50))) ;<-- text height as for as in your drawing

       (or adoc (setq adoc (vla-get-activedocument (setq acapp (vlax-get-acad-object)))))
      (or acsp (setq acsp (vla-get-block (vla-get-activelayout adoc))))
(setq acCol (vla-GetInterfaceObject acapp(strcat "AutoCAD.AcCmColor." (itoa(atoi(getvar "acadver"))))))
      (setq pt (getpoint "\nSpecify table location:"))
      (setq atable (vla-addtable
             acsp
             (vlax-3d-point pt)
             (+ 2 (length tabledata))
             (length headers)
             (* txtheight 1.2)
             (* txtheight 20)
           )
      )
      (vla-put-regeneratetablesuppressed atable :vlax-true)
       ;; calculate column widths : 
      (setq swap (append (list headers) tabledata)
        widths nil)
      (while (car swap)
    (setq column (mapcar 'car swap))
    (setq colwidth (* 1.2 (apply 'max (mapcar 'strlen column))txtheight))
    (setq widths (cons colwidth widths))
    (setq swap (mapcar 'cdr swap)))

      (setq widths (reverse widths))
       ;; set column widths
       (setq col 0)
       (foreach wid widths
         (vla-setcolumnwidth atable col wid)
         (setq col (1+ col))
         )
      (vla-put-horzcellmargin atable (* txtheight 0.5))
      (vla-put-vertcellmargin atable (* txtheight 0.3))
      (vla-setTextheight atable 1 txtheight)
      (vla-setTextheight atable 2 txtheight)
      (vla-setTextheight atable 4 txtheight)
      (vla-setText atable 0 0 "ATTRIBUTE SUMMARY")
      (vla-SetCellAlignment atable 0 0 acMiddleCenter)
            (vla-put-colorindex accol 2)
(vla-setcellcontentcolor atable 0 0 accol)
      (setq col -1)
      (foreach descr headers
    (vla-setText atable 1 (setq col (1+ col)) descr)
    (vla-SetCellAlignment atable 1 col acMiddleCenter)
    (vla-setcellcontentcolor atable 1 col accol)
      )
      
        (vla-put-colorindex accol 4)
      
       (setq row 2)
      
      (foreach record tabledata

    (setq col 0)
    (foreach item record
      (vla-setText atable row col item)
      (if (= 1 col)
      (vla-SetCellAlignment atable row col acMiddleCenter)
      (vla-SetCellAlignment atable row col acMiddleLeft)
           )
      (vla-setcellcontentcolor atable row col accol)
      (setq col (1+ col))
    )
    (setq row (1+ row))
      )
(vla-put-width atable (apply '+ widths))
      (vla-put-height atable (* 1.2 (vla-get-rows atable)txtheight))
      (vla-put-regeneratetablesuppressed atable :vlax-false)

    )
  )
 (if  accol (vlax-release-object accol))
 (if  acapp (vlax-release-object acapp))
(princ)
)
(prompt "\n\t---\tStart command with COUNTATT\t---\n")
(prin1)
 (or (vl-load-com))
(princ)

 

 

It works great, but i need it a Little different.

 

Is it possible to include Mleader with block and attributes?

 

And it would be great if it takes (from the atached blocks) the Attribute Position as type and Count the other two attributes. I mean like:

 

Pos.12 :   23x 83cm / 25x 125cm

 

Thank you in advance...

0 Likes
Accepted solutions (3)
6,242 Views
51 Replies
Replies (51)
Message 21 of 52

ВeekeeCZ
Consultant
Consultant
Accepted solution

I'm unable to reproduce your result...

But I've found some issue.. so try the fixed code and see.

 

I really don't understand your friday's task - no dwg, no examples, probably wrong block (wrong order of att, wrong tag?)... can't help.

 

And most importantly - you should really learn the code and understand the algorithm. I know it's quite complicated, but you can't simply introduce new block every other week and Beekee, please... it ain't working anymore. Grab the book and get into it!

 

(vl-load-com)

(defun c:COUNTATT (/ :sortlst acapp acol acsp adoc atable attdata attitem atts blkdata blkname blkobj col
                   column colwidth  datalist en headers pt row ss swap  tabledata tags total txtheight widths x flt)
  
  ;; Lee Mac
  ;; http://www.lee-mac.com/attributefunctions.html
  
  (defun LM:vl-getattributes ( blk )
    (mapcar '(lambda (att) (cons (vla-get-tagstring att) (vla-get-textstring att)))
            (vlax-invoke blk 'getattributes)))
  
  
  ;; Lee Mac
  ;; https://www.theswamp.org/index.php?topic=46547.0
  (defun LM:vl-getmleaderatts ( obj flt / rtn)
    (if (and (= "AcDbMLeader"  (vla-get-objectname  obj))
             (= acblockcontent (vla-get-contenttype obj))
	     (wcmatch (strcase (vla-get-contentblockname obj)) (strcase flt))
	     )
      (vlax-for sub (vla-item (vla-get-blocks (vla-get-document obj))
                              (vla-get-contentblockname obj))
        (if (= "AcDbAttributeDefinition" (vla-get-objectname sub))
          (setq rtn (cons (cons (vla-get-tagstring sub)
                                (vla-getblockattributevalue obj (LM:objectid sub)))
                          rtn)))))
    (reverse rtn))
  
  ;; ObjectID  -  Lee Mac
  ;; Returns a string containing the ObjectID of a supplied VLA-Object
  ;; Compatible with 32-bit & 64-bit systems
  
  (defun LM:objectid ( obj )
    (eval (list 'defun 'LM:objectid '(obj)
                (if (and (wcmatch (getenv "PROCESSOR_ARCHITECTURE") "*64*")
                         (vlax-method-applicable-p (vla-get-utility (LM:acdoc)) 'getobjectidstring))
                  (list 'vla-getobjectidstring (vla-get-utility (LM:acdoc)) 'obj ':vlax-false)
                  '(itoa (vla-get-objectid obj)))))
    (LM:objectid obj))
  
  ;; Active Document  -  Lee Mac
  ;; Returns the VLA Active Document Object
  
  (defun LM:acdoc nil
    (eval (list 'defun 'LM:acdoc 'nil (vla-get-activedocument (vlax-get-acad-object))))
    (LM:acdoc))
  
;|

;sortlst:

'(
(("POS." . "51") ("83CM" . "40") ("125CM" . "65"))
(("POS." . "51") ("83CM" . "45") ("125CM" . "65"))
(("POS." . "50") ("83CM" . "30") ("125CM" . "75"))
(("POS." . "50") ("83CM" . "45") ("125CM" . "75"))
(("POS." . "52") ("125CM" . "75")))

;to

'(
(("POS." . "50") ("83CM" . "75") ("125CM" . "150"))
(("POS." . "51") ("83CM" . "85") ("125CM" . "130"))
(("POS." . "51") ("83CM" . "")   ("125CM" . "75"))
)
|;
  
  (defun :sortlst (lst / nlst mlst)
    
    (foreach e lst
      (if (assoc "POS." e)
        
        (setq nlst (if (setq a (assoc (assoc "POS." e) nlst))
                     (subst (progn
                              (setq mlst a)
                              (foreach ee (vl-remove-if (function (lambda (y) (wcmatch (car y) "POS."))) e)
                                (setq mlst (if (setq aa (assoc (car ee) mlst))
                                             (subst (cons (car ee) (itoa (+ (atoi (cdr aa))
                                                                            (atoi (cdr ee)))))
                                                    aa
                                                    mlst)
                                             (reverse (cons ee (reverse mlst))))))
                              mlst)
                            a
                            nlst)
                     (cons e nlst)))))
    
    (setq hlst (cons "POS." (mapcar '(lambda (z) (strcat (itoa z) "CM"))		; headers
                                    (cdr (vl-sort (mapcar 'atoi				; headers filter out - accepts any #*
                                                          (mapcar 'car
                                                                  (apply 'append
                                                                         lst)))
                                                  '<)))))
    
    (mapcar '(lambda (x) (mapcar '(lambda (y) (cond ((assoc y x))
                                                    ((cons y ""))))
                                 hlst))
            (reverse nlst))
    )
  
  
  ; ----------------------------------------------------------------------------------------------------------------------------
  ; ----------------------------------------------------------------------------------------------------------------------------
  
  (setq flt "*")			; block filter
  
  (if (setq ss (ssget (list '(-4 . "<OR")
			    '(-4 . "<AND") '(0 . "INSERT") (cons 2 flt) '(66 . 1) '(-4 . "AND>")
			    '(0 . "MULTILEADER") '(-4 . "OR>"))))
    (progn
      (repeat (setq i (sslength ss))
        (setq obj (vlax-ename->vla-object (ssname ss (setq i (1- i))))
              tabledata (cons (if (= "AcDbMLeader" (vla-get-objectname  obj))
                                (LM:vl-getmleaderatts obj flt)
                                (LM:vl-getattributes obj))
                              tabledata)))
      
      (setq tabledata (vl-remove nil tabledata)
	    tabledata (:sortlst tabledata)
            headers (mapcar 'car (car tabledata))
            tags    headers
            tabledata (mapcar '(lambda (x) (mapcar 'cdr x))
                              tabledata))
      
      ;; sort by "A1" :
      
      (setq tabledata (vl-sort tabledata '(lambda (a b)(< (car a)(car b)))))
      
      (setq total 0)
      (foreach i datalist (setq total (+ total (cdr i))))
      
      (initget 6)
      (setq txtheight (cond ((getreal "\nSpecify Text height for the table <50>: "))
                            (50))) ;<-- text height as for as in your drawing
      
      (or adoc (setq adoc (vla-get-activedocument (setq acapp (vlax-get-acad-object)))))
      (or acsp (setq acsp (vla-get-block (vla-get-activelayout adoc))))
      (setq acCol (vla-GetInterfaceObject acapp(strcat "AutoCAD.AcCmColor." (itoa (atoi (getvar "acadver"))))))
      
      (setq pt (getpoint "\nSpecify table location:")
            atable (vla-addtable
                     acsp
                     (vlax-3d-point pt)
                     (+ 2 (length tabledata))
                     (length headers)
                     (* txtheight 1.2)
                     (* txtheight 20)))
      
      (vla-put-regeneratetablesuppressed atable :vlax-true)
      
      ;; calculate column widths :
      (setq swap (append (list headers) tabledata)
            widths nil)
      (while (car swap)
        (setq column (mapcar 'car swap)
              colwidth (* 1.2 (apply 'max (mapcar 'strlen column)) txtheight)
              widths (cons colwidth widths)
              swap (mapcar 'cdr swap)))
      
      (setq widths (reverse widths))
      ;; set column widths
      (setq col 0)
      
      (foreach wid widths
        (vla-setcolumnwidth atable col wid)
        (setq col (1+ col)))
      
      (vla-put-horzcellmargin atable (* txtheight 0.5))
      (vla-put-vertcellmargin atable (* txtheight 0.3))
      (vla-setTextheight atable 1 txtheight)
      (vla-setTextheight atable 2 txtheight)
      (vla-setTextheight atable 4 txtheight)
      (vla-setText atable 0 0 "ATTRIBUTE SUMMARY")
      (vla-SetCellAlignment atable 0 0 acMiddleCenter)
      (vla-put-colorindex accol 2)
      (vla-setcellcontentcolor atable 0 0 accol)
      
      (setq col -1)
      (foreach descr headers
        (vla-setText atable 1 (setq col (1+ col)) descr)
        (vla-SetCellAlignment atable 1 col acMiddleCenter)
        (vla-setcellcontentcolor atable 1 col accol))
      
      (vla-put-colorindex accol 4)
      
      (setq row 2)
      
      (foreach record tabledata
        (setq col 0)
        (foreach item record
          (vla-setText atable row col item)
          (if (= 1 col)
            (vla-SetCellAlignment atable row col acMiddleCenter)
            (vla-SetCellAlignment atable row col acMiddleLeft))
          (vla-setcellcontentcolor atable row col accol)
          (setq col (1+ col)))
        (setq row (1+ row)))
      
      (vla-put-width atable (apply '+ widths))
      (vla-put-height atable (* 1.2 (vla-get-rows atable)txtheight))
      (vla-put-regeneratetablesuppressed atable :vlax-false)))
  
  (if  accol (vlax-release-object accol))
  (if  acapp (vlax-release-object acapp))
  (princ)
  )

(prompt "\n\t---\tStart command with COUNTATT\t---\n")
(princ)

 

0 Likes
Message 22 of 52

C.Utzinger
Collaborator
Collaborator

Good morning

 

Thank you i will try the code.

 

I know i was to (i don´t know a good word) "impudent". I can Show my hole code whith dcl if you want, it's not that i'm doing nothing.

 

I tried several times now to understand the code, but for now there was now way.

 

I will understand if you don´t want to help me anymore and i will not ask again.

 

Thank you anyway and enjoy the chocolate :), you made very much for me.

 

Kind regards

 

 

0 Likes
Message 23 of 52

ВeekeeCZ
Consultant
Consultant

Hi,
it's not about my wiliness to help you but the fact that I can't keep up with your development.

 

You have introduced the third version of the block in couple of weeks and since the code is not versatile enough, every change has some effect on the code and the code must be rewritten. If you don't understand the code, you're unable to decide how your another block enhancement should be made to minimize the code adjustments. Then it's up to me (or whoever else) to rewrite the code over and over again - very likely more complicated than could be necessary. And since we don't know your further intentions we can't recommend you anything.

You really need to improve your LISP programming skills - It's not that complicated, but it takes some time and effort to get into it. I think that you are on the right path already... 😉

0 Likes
Message 24 of 52

ВeekeeCZ
Consultant
Consultant

BTW Do you understand the code from post #10? If so I could rewrite the main :sortlst function into two parts which could be easier to understand. Do you think it could help you?

0 Likes
Message 25 of 52

C.Utzinger
Collaborator
Collaborator

Thank you!

 

I understand what it does, i have to search some time for completly understand it, than i will answer again.

 

 

On Friday i thought i had finished the tool, but at the last moment i realized that this Product "Ferbox" is a little different. After that it will be finished.

 

I want to try fix it on my own, but i will need some time for it.

 

 

Sorry again...

 

Kind regards

Message 26 of 52

C.Utzinger
Collaborator
Collaborator

OK!

 

I think i now understand the code from Post 10.

 

What is the next step?

0 Likes
Message 27 of 52

ВeekeeCZ
Consultant
Consultant

Ok then, here you go. Create a function which sorts the first list to the second.

 

'(
  (50 ("83CM" . "30") ("125CM" . "75"))
  (50 ("83CM" . "45") ("125CM" . "75"))
  (51 ("80CM" . "40") ("122CM" . "65"))
  (51 ("83CM" . "45") ("125CM" . "65"))
  (52 ("125CM" . "75"))
  )

;TO

'(
  (50 ("83CM" . "30") ("125CM" . "75") ("83CM" . "45") ("125CM" . "75"))
  (51 ("80CM" . "40") ("122CM" . "65") ("83CM" . "45") ("125CM" . "65"))
  (52 ("125CM" . "75"))
  )
Message 28 of 52

C.Utzinger
Collaborator
Collaborator

 

I think i have it!

 

(defun c:<Test5 ( / asoc asoc2 olst nlst mlst)

	(setq olst '((50 ("83CM" . "30") ("125CM" . "75"))
  		     (50 ("83CM" . "45") ("125CM" . "75"))
  		     (51 ("80CM" . "40") ("122CM" . "65"))
  		     (51 ("83CM" . "45") ("125CM" . "65"))
  		     (52 ("125CM" . "75"))))

  (foreach item olst
    (if (setq asoc (assoc (car item) nlst))
      (setq nlst (subst (cons (car item)
                              (cons (cdr asoc)(cdr item)))
                        asoc
                        nlst))
      (setq nlst (cons item nlst))))
  (reverse nlst)
) ; end of defun
0 Likes
Message 29 of 52

ВeekeeCZ
Consultant
Consultant

I don't, sorry! Nice try though. Ever heard of the append function?

 

'(
  (50 (("83CM" . "30") ("125CM" . "75")) ("83CM" . "45") ("125CM" . "75"))
  (51 (("80CM" . "40") ("122CM" . "65")) ("83CM" . "45") ("125CM" . "65"))
  (52 ("125CM" . "75"))
  )
0 Likes
Message 30 of 52

C.Utzinger
Collaborator
Collaborator

 

Ups, i didn't saw it!

 

Append? No but i will look for it!

 

Thank you

0 Likes
Message 31 of 52

C.Utzinger
Collaborator
Collaborator

Now?????

 

(defun c:<Test3 ( / asoc asoc2 olst nlst)

	(setq olst '((50 ("83CM" . "30") ("125CM" . "75"))
  		     (50 ("83CM" . "45") ("125CM" . "75"))
  		     (51 ("80CM" . "40") ("122CM" . "65"))
  		     (51 ("83CM" . "45") ("125CM" . "65"))
  		     (52 ("125CM" . "75"))))

  (foreach item olst
    (if (setq asoc (assoc (car item) nlst))
      (setq nlst (subst (cons (car item)
                              (append (cdr asoc)(cdr item)))
                        asoc
                        nlst))
      (setq nlst (cons item nlst))))
  (reverse nlst)
) ; end of defun
Message 32 of 52

ВeekeeCZ
Consultant
Consultant

You know the result is correct, but you can save 5 functions!

0 Likes
Message 33 of 52

C.Utzinger
Collaborator
Collaborator

I forgot to take away the "progn"... but 4 functions?

0 Likes
Message 34 of 52

ВeekeeCZ
Consultant
Consultant

Stretch your brain little bit!!

 

Here you have a list of them, alphabetically.

 

car, cdr, cons, your progn and setq ... and an extra kudo for you if you'll find one more!

0 Likes
Message 35 of 52

C.Utzinger
Collaborator
Collaborator

Sorry! No idea! I will look at it tomorrow.

 

Good night

0 Likes
Message 36 of 52

C.Utzinger
Collaborator
Collaborator

WTF! I don't see it.

 

Please give me a hint.

Or should i rewrite the whole code differently?

 

Regards

0 Likes
Message 37 of 52

ВeekeeCZ
Consultant
Consultant

There is really nothing much to add.

 

But here is very common example. Let's say, you need to build a layer filter for the (ssget) function with layer from the other selected entity.

So layer filter needs to be a pair, something like this '(8 . "LayerName")... but you need to get the layer from an entity... so you use..

 

(cdr (assoc 8 (entget (car (entsel)))))...

 

then you need to make a pair...

(cons 8 (cdr (assoc 8 (entget (car (entsel))))))


But is it really all that necessary?

 

Anyway, you'll got a whole sunny weekend in front of you, would not be nice to spend it laying by the lake with a (the!) book? 😉

0 Likes
Message 38 of 52

C.Utzinger
Collaborator
Collaborator

Are you thinking about VLisp?

 

It would be nice to lay by the lake.

I could send you a foto from my daughter (15 months) looking at me with sad face, because i'm all the morning in front of the computerSmiley Very Happy.

We have 4 days off, but my wife is working all days (Airport!), so i'm looking all day after "Speedy Gonzales".

 

Thank you...

 

 

 

0 Likes
Message 39 of 52

ВeekeeCZ
Consultant
Consultant

Well, life is challenging for all of us - that was the heavy card you've played.. maybe add some pets and I'm finished.

 

Anyway, here you have your code.

 

(foreach item olst
    (setq nlst (if (setq asoc (assoc (car item) nlst))
                 (subst (append asoc (cdr item))
                        asoc
                        nlst)
                 (cons item nlst))))

 

 

 

 

0 Likes
Message 40 of 52

C.Utzinger
Collaborator
Collaborator
LOL. Ok the cat is also looking at me😂😂😂😂.
I just asked you for a hint so i could try again.
Whats next? Try to sum up?

Kind regards
0 Likes