Author Topic: Lisp to count text in drawing  (Read 3484 times)

0 Members and 1 Guest are viewing this topic.

JohnK

  • Administrator
  • Seagull
  • Posts: 10603
Lisp to count text in drawing
« on: December 06, 2021, 03:37:34 PM »
I saw a post over at cadtutor.net but I cannot find my login (must have been too many years since I've posted there) but I did the work answering the post so I thought I'd post it here instead.

The OP wanted to count the text in a drawing and list their layers. Here is my quick and dirty answer.

Using:
Code - Auto/Visual Lisp: [Select]
  1. ;; General GETK { get key(s) value(s) from list(s) } function
  2. ;; inspired by Tony Tanzillo's (get key_or_keys_list from_list)
  3. ;; from CHELEV.LSP on R12 Bonus CD, but
  4. ;; goes much further in recursion on both KEYs and LISTs
  5. ;; (thus retaining their structure upon return)
  6. ;;
  7. ;; by Vladimir Nesterovsky 1996-1997
  8. (defun GETK (k l)      ;;;;; GET KEY(s) FROM LIST(s)
  9.   (if (atom (caar l)) ;; l is ASSOC'able list
  10.     (cond             ;; use this l!
  11.       ((atom k)       ;; k is a key
  12.         (get1 k l))
  13.       ((and (cdr k)(atom (cdr k))) ;; '(0 . 8) -->> ("ENTITY" . "LAYER")
  14.         (cons (getk (car k) l) (get1 (cdr k) l)))
  15.       (T              ;; k is a list of something - get inside
  16.         (mapcar '(lambda(subk)(getk subk l)) k) )
  17.     )                 ;; else - get inside list
  18.     (mapcar '(lambda(subl)(getk k subl)) l)
  19. ))
  20. (defun get1 (a b) (cdr (assoc a b)))
  21.  
  22. (defun sel2lst ( sel / l len )
  23.   (if (= 'PICKSET (type sel))
  24.    (repeat (setq len (sslength sel))
  25.     (setq len (1- len) l (cons (ssname sel len) l)))))


You can then just get a list of text and layer.
Code - Auto/Visual Lisp: [Select]
  1. (getk
  2.   '(1 8)
  3.   (mapcar
  4.     'entget
  5.     (sel2lst
  6.       (ssget"X" '((0 . "TEXT,MTEXT"))))))

After that, do what you want with it like, print off the list (or something else more useful).

For example, make an ugly print of the entries.
Code - Auto/Visual Lisp: [Select]
  1. ( (lambda (/ alist)
  2.  
  3.     (setq alist
  4.           (getk
  5.             '(1 8)
  6.             (mapcar 'entget (sel2lst (ssget"X" '((0 . "TEXT,MTEXT")))))))
  7.  
  8.     (if alist
  9.       (progn
  10.  
  11.         (princ "\nTEXT\t\tLAYER")
  12.         (mapcar
  13.           (function
  14.             (lambda (lst)
  15.               (write-line
  16.                 (apply 'strcat
  17.                        (mapcar
  18.                          (function
  19.                            (lambda (x)
  20.                              (strcat x "\t\t")))
  21.                          lst
  22.                          )
  23.                        )
  24.                 )
  25.               )
  26.             )
  27.           alist
  28.           )
  29.         (princ (strcat "Total: \t\t" (length (itoa alist))))
  30.         )
  31.       ) ;if
  32.     (princ)
  33.     )
  34.  )
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Tharwat

  • Swamp Rat
  • Posts: 707
  • Hypersensitive
Re: Lisp to count text in drawing
« Reply #1 on: December 06, 2021, 04:28:07 PM »
Here is my attempt which returns a list as shown below and hopefully they won't have Mtexts formatted otherwise unformatting function required to strip strings.
Code - Auto/Visual Lisp: [Select]
  1. (and (setq s (ssget "_X" '((0 . "TEXT,MTEXT"))))
  2.      (repeat (setq i (sslength s))
  3.        (setq i (1- i)
  4.              e (entget (ssname s i))
  5.              l (list (cdr (assoc 1 e)) (cdr (assoc 8 e)))
  6.              )
  7.        (or (and (vl-some (function (lambda (q) (and (= (car q) (car l)) (= (cadr q) (cadr l)) (setq r q)))) g)
  8.                 (setq g (vl-remove r g)
  9.                       g (cons (append l (list (1+ (caddr r)))) g)
  10.                       )
  11.                 )
  12.            (setq g (cons (append l (list 1)) g))
  13.            )
  14.        )
  15.      )
  16.  
  17. ;; Returns:
  18. ;; (("Abc" "Layer1" 20) ("xyz" "Layer2" 3) ("tt" "Layer2" 1) ("xx" "Walls" 1))
  19.  

ancrayzy

  • Mosquito
  • Posts: 12
Re: Lisp to count text in drawing
« Reply #2 on: December 06, 2021, 08:52:27 PM »
Hi John Kaul (Se7en),
I am a writer on cadtutor.net,
https://www.cadtutor.net/forum/topic/74116-lisp-to-count-text-and-layers/
Thank you very much for this article.
I saw the link and saw your post here but I still don't know how to combine the above code.
Do I just copy and combine all the code in that post into a single lisp and use it

Regards,
« Last Edit: December 06, 2021, 09:08:37 PM by ancrayzy »

JohnK

  • Administrator
  • Seagull
  • Posts: 10603
Re: Lisp to count text in drawing
« Reply #3 on: December 06, 2021, 09:25:37 PM »
Put the first block and either the second block--or third block--into a file and load into AutoCAD with "appload".

The first block of code is the support functions. The second code block is the actual "usage code" (this is how you use the support functions in the first block of code). The third block of code represents an example; it represents a procedure you are writing (it is just another example).

Ultimately (if you use the code I or Thwart posted), you need to create a function you can call from the command line. I--or many others here--can teach you how to write a function if you want to learn. However, if you only want me to create a ready made solution, please understand that I don't like posting ready made functions for people (I would much rather teach you to fish on your own then give you a fish every time you're hungry; that is how you build a community).


Hi John Kaul (Se7en),
I am a writer on cadtutor.net,
https://www.cadtutor.net/forum/topic/74116-lisp-to-count-text-and-layers/
Thank you very much for this article.
I saw the link and saw your post here but I still don't know how to combine the above code.
Do I just copy and combine all the code in that post into a single lisp and use it

Regards,
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

ancrayzy

  • Mosquito
  • Posts: 12
Re: Lisp to count text in drawing
« Reply #4 on: December 07, 2021, 12:01:12 AM »
Thank you very much for your reply Mr John Kaul (Se7en), it makes a lot of sense.
I put the code together and couldn't find the command line, that's why I need help with a complete lisp file.
And another reason is that I don't know how to write auto lisp command  :embarrassed2:

JohnK

  • Administrator
  • Seagull
  • Posts: 10603
Re: Lisp to count text in drawing
« Reply #5 on: December 07, 2021, 10:36:19 AM »
We all were new to lisp at one point. Would you like to learn?

I noticed from your latest post on cadtutor, you thought about adding to the table generated by LeeMac's code. You already have enough pieces of the puzzle to do that. -i.e. I took a few pieces of code from his routine and what I posted above and got something working in 5 minutes.

I could post you a final product but that isn't doing you or I any good because the next time you need to modify it or need another solution you will just ask me to do it for you. That's not a community! I want you to learn so you and I can post ideas and concepts and make something super cool. 
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

ancrayzy

  • Mosquito
  • Posts: 12
Re: Lisp to count text in drawing
« Reply #6 on: December 07, 2021, 08:23:48 PM »
Thank you Mr Se7en

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8659
  • AKA Daniel
Re: Lisp to count text in drawing
« Reply #7 on: December 08, 2021, 12:35:30 AM »
in c++
doesn't include blocks n attributes n stuff


Code - C: [Select]
  1. static void ArxBrxTest_doit(void)
  2.     {
  3.         std::map<AcString, std::map<AcString, size_t>> layerMap;
  4.         const ACHAR* prompt[] = { ACRX_T("\nSelect: "), ACRX_T("\nRemove: ") };
  5.         SelectionSet set(ACRX_T(":$"), prompt, NULL, NULL);
  6.         for (auto id : set.Select())
  7.         {
  8.             if (id.objectClass()->isDerivedFrom(AcDbText::desc()))
  9.             {
  10.                 if (AcDbObjectPointer<AcDbText> textPtr(id); textPtr.openStatus() == eOk)
  11.                     layerMap[textPtr->layer()][textPtr->textString()]++;
  12.             }
  13.             if (id.objectClass()->isDerivedFrom(AcDbMText::desc()))
  14.             {
  15.                 if (AcDbObjectPointer<AcDbMText> textPtr(id); textPtr.openStatus() == eOk)
  16.                     layerMap[textPtr->layer()][textPtr->text()]++;
  17.             }
  18.             if (id.objectClass()->isDerivedFrom(AcDbMLeader::desc()))
  19.             {
  20.                 if (AcDbObjectPointer<AcDbMLeader> textPtr(id); textPtr.openStatus() == eOk)
  21.                 {
  22.                     std::unique_ptr<AcDbMText> ptr(textPtr->mtext());
  23.                     layerMap[textPtr->layer()][ptr->text()]++;
  24.                 }
  25.             }
  26.         }
  27.         for (auto& i : layerMap)
  28.         {
  29.             for (auto& j : i.second)
  30.                 acutPrintf(_T("\nLayer %ls, Text = %ls, Count = %ld"), i.first, j.first, j.second);
  31.         }
  32.     }
  33.  

Code: [Select]
Layer 1_CRP_TEXT, Text = BUTT JOINT WITH BISCUIT, Count = 1
Layer 1_CRP_TEXT, Text = Base Not by C-tec, Count = 1
Layer 1_CRP_TEXT, Text = Birch Case work, Count = 1
Layer 1_CRP_TEXT, Text = Birch Casework, Count = 16
Layer 1_CRP_TEXT, Text = Birch Chair Rail, Count = 3
Layer 1_CRP_TEXT, Text = Birch Door, Count = 2
Layer 1_CRP_TEXT, Text = Birch Drawer Faces, Count = 1
Layer 1_CRP_TEXT, Text = Birch Interior, Count = 1
Layer 1_CRP_TEXT, Text = Birch Panels, Count = 1
Layer 1_CRP_TEXT, Text = Birch Support, Count = 1
Layer 1_CRP_TEXT, Text = Birch Veneer, Count = 1
Layer 1_CRP_TEXT, Text = Birch veneer base, Count = 1
Layer 1_CRP_TEXT, Text = Birch veneer over 3/4" MDF, Count = 1
Layer 1_CRP_TEXT, Text = Blocking BY GC, Count = 3
Layer 1_CRP_TEXT, Text = Blocking By GC, Count = 1
Layer 1_CRP_TEXT, Text = Blum File system, Count = 1
Layer 1_CRP_TEXT, Text = Butt Joint, Count = 1
Layer 1_CRP_TEXT, Text = C-TEC WILL PROVIDE GC A 1/2" PARTICAL BOARD TEMPLATE OF GLASS , Count = 2
Layer 1_CRP_TEXT, Text = Column, Count = 1
Layer 1_CRP_TEXT, Text = Continuous attachment cleat, Count = 1
Layer 1_CRP_TEXT, Text = Dado For Glass, Count = 1
Layer 1_CRP_TEXT, Text = Dark Stained  Reveals, Count = 2
Layer 1_CRP_TEXT, Text = Dark Stained 1/4" Reveals, Count = 1
Layer 1_CRP_TEXT, Text = Dark Stained Birch, Count = 1
Layer 1_CRP_TEXT, Text = Dark Stained Birch

sorry, I'll go back to my corner

edit... ah!!! how about the case
Layer 1_CRP_TEXT, Text = Blocking BY GC, Count = 3
Layer 1_CRP_TEXT, Text = Blocking By GC, Count = 1
« Last Edit: December 08, 2021, 12:57:28 AM by It's Alive! »

ancrayzy

  • Mosquito
  • Posts: 12
Re: Lisp to count text in drawing
« Reply #8 on: December 08, 2021, 01:38:44 AM »
Thank Mr It's Alive!
Currently, I still use the data extraction feature to count texts and layers but it takes a lot more time than using autolisp  :embarrassed2:

JohnK

  • Administrator
  • Seagull
  • Posts: 10603
Re: Lisp to count text in drawing
« Reply #9 on: December 08, 2021, 10:03:51 AM »
Lisp writing crash coarse

1. In windows explorer create a new text file and call it "string-count.lsp"

2. Open string-count.lsp in your text editor, VSCode, or the VLIDE, or...

3. Copy the first three functions from this post:
  "GETK"
  "get1"
  "sel2lst"
  and paste them into the string-count.lsp

4. Open a copy of TCountV1-1.lsp and find the routines (copy and paste them into string-count.lsp).
   "LM:AddTable"
   "_startundo"
   "_endundo"

5. Save the string-count.lsp

6. At the bottom of the string-count.lsp file create start a new function:
    NOTE: Type--DO NOT COPY AND PASTE--the following:

Code - Auto/Visual Lisp: [Select]
  1. (defun c:stringcount ( / )
  2.    (setq *acdocument*
  3.          *acdocumentspace*
  4.            (vlax-get-property
  5.               *acdocument*
  6.                 (if (= 1 (getvar 'CVPORT))
  7.                   'Paperspace
  8.                   'Modelspace
  9.                   ) ;_if
  10.                 ) ;_vlax-get-property
  11.            ) ;_setq
  12.      (_StartUndo *acdocument*)
  13.  
  14.      ;;
  15.  
  16.      (_EndUndo *acdocument*)
  17.   ) ;_defun
       
7. We now--technically--have a working lisp file so it is time to start documenting our code. At the top of the file create the following:

Code: [Select]
;;;===================================================================
;;; string-count
;;;-------------------------------------------------------------------
;;; Date:           2021.12.08
;;; Author:         ancrayzy
;;;-------------------------------------------------------------------
;;; This will count strings in the drawing and create a table showing
;;; the text string and the layer the text resides on.
;;;
;;; Inspiration and code has been taken from:
;;;   Vladimir Neterovsky
;;;   LeeMac
;;;===================================================================

8. Save the file.

9. Back to our stringcount function at the bottom. In between the start and end undo lines add the following (type do not copy/paste).

Code - Auto/Visual Lisp: [Select]
  1. (setq textkeylist
  2.       (getk '(1 8)
  3.             (mapcar
  4.                 'entget
  5.                    (sel2lst
  6.                      (ssget"X" '((0 . "TEXT,MTEXT")))))
  7.             ) ;_getk
  8.       ) ;_setq
  9.  (if (apply 'and textkeylist)
  10.     (setq textkeylist
  11.          (cons (list "String" "Layer") textkeylist))
  12.     (quit)
  13.     )
  14.  (setq point (getpoint "\nSpecify point for table: "))
  15.  (LM:AddTable
  16.       *acdocumentspace*
  17.       (trans point 1 0)
  18.       "STRING COUNT"
  19.       textkeylist
  20.       )
     
10. On the first line of our function we need to localize our variables. we do that by typing the variable names after a forward slash.

Code - Auto/Visual Lisp: [Select]
  1. (defun c:stringcount ( / *acdocument* *acdocumentspace*
  2.                          textkeylist point )

11. Our lisp is done but you can add a prompt to the bottom of the lisp to inform us the command to issue when we load the file.

Code - Auto/Visual Lisp: [Select]
  1. (princ "\nstring count lisp loaded. Type [stringcount] to
  2. begin.")

12. Post your version of the "c:stringcount" function when you are done.

13. Ask questions!
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

ancrayzy

  • Mosquito
  • Posts: 12
Re: Lisp to count text in drawing
« Reply #10 on: December 08, 2021, 09:47:52 PM »
Thank you Mr Se7en for your Tuts,
I following these step and there is my first lisp of my life  :lol:, but it don't work  :-P
Quote
;;;===================================================================
;;; String-count
;;;-------------------------------------------------------------------
;;; Date:           2021.12.09
;;; Author:         ancrayzy
;;;-------------------------------------------------------------------
;;; This will count strings in the drawing and create a table showing
;;; the text string and the layer the text resides on.
;;;
;;; Inspiration and code has been taken from:
;;;   Vladimir Neterovsky
;;;   LeeMac
;;;===================================================================

(defun c:stringcount ( /  *acdocument* *acdocumentspace*)
textkeylist point )
   (setq *acdocument*
           (vla-get-activedocument (vlax-get-acad-object))
         *acdocumentspace*
           (vlax-get-property
              *acdocument*
                (if (= 1 (getvar 'CVPORT))
                  'Paperspace
                  'Modelspace
                  ) ;_if
                ) ;_vlax-get-property
           ) ;_setq
     (_StartUndo *acdocument*)

(setq textkeylist
      (getk '(1 8)
            (mapcar
                'entget
                   (sel2lst
                     (ssget"X" '((0 . "TEXT,MTEXT")))))
            ) ;_getk
      ) ;_setq
 (if (apply 'and textkeylist)
    (setq textkeylist
         (cons (list "String" "Layer") textkeylist))
    (quit)
    )
 (setq point (getpoint "\nSpecify point for table: "))
 (LM:AddTable
      *acdocumentspace*
      (trans point 1 0)
      "STRING COUNT"
      textkeylist
      )
 
     (_EndUndo *acdocument*)
  ) ;_defun

(princ "\nstring count lisp loaded. Type [stringcount] to
begin.")
« Last Edit: December 08, 2021, 09:51:03 PM by ancrayzy »

BIGAL

  • Swamp Rat
  • Posts: 1396
  • 40 + years of using Autocad
Re: Lisp to count text in drawing
« Reply #11 on: December 08, 2021, 11:05:24 PM »
1st error I use notepad++ yes that is the name of the software, it has a lisp checking function, it shows starting and closing Bracket pairs. So your code stops after 1st line. I did not test after fixing.



(defun c:stringcount ( /  *acdocument* *acdocumentspace* textkeylist point )
A man who never made a mistake never made anything

ancrayzy

  • Mosquito
  • Posts: 12
Re: Lisp to count text in drawing
« Reply #12 on: December 09, 2021, 01:45:01 AM »
I modify the lisp as per GIGAL's answer but it still doesn't work. The following results
Code: [Select]
Command: AP
APPLOAD STRINGS COUNT.LSP successfully loaded.


Command:
string count lisp loaded. Type [stringcount] to begin.
Command:
Command: STRINGCOUNT
; error: no function definition: _STARTUNDO

JohnK

  • Administrator
  • Seagull
  • Posts: 10603
Re: Lisp to count text in drawing
« Reply #13 on: December 09, 2021, 07:54:38 AM »
I modify the lisp as per BIGAL's answer but it still doesn't work. The following results ...

It doesn't work because you forgot to include the other functions from here and Lee's code (steps 3 and 4). But that's close enough. great job!

Please see attached. 
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

ancrayzy

  • Mosquito
  • Posts: 12
Re: Lisp to count text in drawing
« Reply #14 on: December 12, 2021, 08:55:16 PM »
Thank you Mr Se7en,
There are still a few issues.
1. Lisp runs itself after finishing the command and does not ask the user to select the area to be processed.
2. Lisp auto counts all the text in the drawing.
3. Results do not combine objects with the same name and layer as "TCountV1-1" (separate in rows).
4. Do not ask the user for the font size of the spreadsheet as "TCountV1-1" when exporting the results.

Here is my test file.
In this file, I use this lisp to count the number of columns in each zone (defined by layer name) of a floor.