FIND TEXT LSP

FIND TEXT LSP

jlaidle1
Advocate Advocate
12,021 Views
36 Replies
Message 1 of 37

FIND TEXT LSP

jlaidle1
Advocate
Advocate

The FIND and REPLACE command in AutoCAD is good, but not for our large drawing files.  What I need is a simple 'find' command (lsp) that will search for a single text value.

 

The FIND and REPLACE command takes too long for our users to close, because it seems to be completing the search.

The more objects in the drawing, the longer it takes to close.

 

Any help would be greatly appreciated.

 

John Laidler
ITO - Application Management


Please use "Accept as Solution" & give "Kudos" if this response helped you.

Accepted solutions (2)
12,022 Views
36 Replies
Replies (36)
Message 21 of 37

john.uhden
Mentor
Mentor
It's a habit of mine. Most often when you build a command function that
does not include a (command ...) or (vl-cmdf ...) call then the routine
closes out with some kinda superfluous message. Maybe it's just with a
cancel; I dunno, but it happens. You don't need it if you invoke a
command, but it doesn't hurt to leave it in.

John F. Uhden

0 Likes
Message 22 of 37

Kent1Cooper
Consultant
Consultant
Accepted solution

@jlaidle1 wrote:

.... the LSP would stop searching after the user selects 'cancel'.


 

That sounds to me like you don't necessarily want to find a whole selection set of such objects and select/highlight/grip them all, but rather want to step through, Zooming to each qualifying object, and exit once you arrive at the one you're looking for.  If so, try this [very lightly tested]:

(defun C:FT ; = Find Text and step through looking at each
  (/ *error* txtstr txtss n txtent)
  (defun *error* (errmsg)
    (if (not (wcmatch errmsg "Function cancelled,quit / exit abort,console break"))
      (princ (strcat "\nError: " errmsg))
    ); if
    (if txtent (redraw txtent 4)); in case of ESC to exit
(princ) ); defun - *error* (setq txtstr (getstring T "\Text content to find {entire or partial, case-sensitive}: ")) (if (setq txtss (ssget "_X" (list (cons 1 (strcat "*" txtstr "*")) (cons 410 (getvar 'ctab))))) (repeat (setq n (sslength txtss)); then (command "_.zoom" "_object" (setq txtent (ssname txtss (setq n (1- n)))) "" "_.zoom" "0.5X" ; back off view for context [EDIT ratio as desired] ); command (redraw txtent 3); highlight (initget "eXit") (if (or (= n 0); got to last one -- don't ask about next-or-Exit (= (getkword "Press Enter/space to Zoom to next, or [eXit] <next>: ") "eXit") ); or (progn (redraw txtent 4) (quit)) ); if (redraw txtent 4); un-highlight ); repeat (prompt "\nNothing found containing that string."); else ); if (princ) ); defun

It highlights the one it has Zoomed around, in case that makes it easier to notice, but it will also be smack in the center of the screen.  When it gets to the one you want, you can eXit the routine either by choosing that option or by hitting ESCape.  It leaves you Zoomed around that object [and un-highlights it], and likewise when it gets to the last one it found, if you haven't exited by then.

 

[Yes, it has the same shortcoming of not accounting for long Mtext with content in multiple 3-code entries, nor does it see Attributes as FIND can, but see whether it does what you want otherwise.]

Kent Cooper, AIA
0 Likes
Message 23 of 37

jlaidle1
Advocate
Advocate

Works great, thanks!!!!

John Laidler
ITO - Application Management


Please use "Accept as Solution" & give "Kudos" if this response helped you.

0 Likes
Message 24 of 37

jlaidle1
Advocate
Advocate

Can this search for text values in blocks also?

 

John Laidler
ITO - Application Management


Please use "Accept as Solution" & give "Kudos" if this response helped you.

0 Likes
Message 25 of 37

Kent1Cooper
Consultant
Consultant

@jlaidle1 wrote:

Can this search for text values in blocks also?


Sorry.... no.  Searches using (ssget) can "see" only top-level entities, not nested things.

 

EDIT:  And would you mean Attribute values, or Text/Mtext objects nested in Blocks?  If the latter, I think a routine would need to open up all Block definitions and step through their pieces looking for such objects, but then when it found one, it would need to step back and do a Search for all insertions of that Block, and add them into the selection set to step through....  And what if that Block is nested inside other Blocks?

Kent Cooper, AIA
0 Likes
Message 26 of 37

jlaidle1
Advocate
Advocate

The text would be attributes.  🙂

John Laidler
ITO - Application Management


Please use "Accept as Solution" & give "Kudos" if this response helped you.

0 Likes
Message 27 of 37

ronjonp
Mentor
Mentor

Try this:

(defun c:foo (/ a ao f o s)
  ;; RJP » 2020-01-27
  (defun _zoom (o ao m / ll pts ur)
    (vla-getboundingbox o 'll 'ur)
    (setq pts (mapcar 'vlax-safearray->list (list ll ur)))
    (vlax-invoke
      ao
      'zoomcenter
      (mapcar '/ (mapcar '+ (car pts) (cadr pts)) '(2 2 2))
      (* m (distance (car pts) (cadr pts)))
    )
    (while (null (getpoint "\nPick a point to find next value: ")))
  )
  (if (and (setq f (getstring t "\nText to find: "))
	   (setq s (ssget "_X"
			  (list	'(-4 . "<OR")
				'(-4 . "<AND")
				'(0 . "insert")
				'(66 . 1)
				'(-4 . "AND>")
				'(0 . "*text")
				'(-4 . "OR>")
				(cons 410 (getvar 'ctab))
			  )
		   )
	   )
      )
    (progn (setq ao (vlax-get-acad-object))
	   (foreach e (mapcar 'cadr (ssnamex s))
	     (setq o (vlax-ename->vla-object e))
	     (if (and (vlax-property-available-p o 'textstring))
	       (and (wcmatch (strcase (vla-get-textstring o)) (strcat "*" (strcase f) "*"))
		    (_zoom o ao 2)
	       )
	       (foreach	a (vlax-invoke o 'getattributes)
		 (and (wcmatch (strcase (vla-get-textstring a)) (strcat "*" (strcase f) "*"))
		      (_zoom a ao 2)
		 )
	       )
	     )
	   )
    )
  )
  (princ)
)
0 Likes
Message 28 of 37

ВeekeeCZ
Consultant
Consultant

@ronjonp wrote:

Try this:

(defun c:foo (/ a ao f o s)
  ;; RJP » 2020-01-27
  (defun _zoom (o ao m / ll pts ur)
    (vla-getboundingbox o 'll 'ur)
    (setq pts (mapcar 'vlax-safearray->list (list ll ur)))
    (vlax-invoke
      ao
      'zoomcenter
      (mapcar '/ (mapcar '+ (car pts) (cadr pts)) '(2 2 2))
      (* m (distance (car pts) (cadr pts)))
    )
    (while (null (getpoint "\nPick a point to find next value: ")))
  )
  (if (and (setq f (getstring t "\nText to find: "))
	   (setq s (ssget "_X"
			  (list	'(-4 . "<OR")
				'(-4 . "<AND")
				'(0 . "insert")
				'(66 . 1)
				'(-4 . "AND>")
				'(0 . "*text,multileader")
				'(-4 . "OR>")
				(cons 410 (getvar 'ctab))
			  )
		   )
	   )
      )
    (progn (setq ao (vlax-get-acad-object))
	   (foreach e (mapcar 'cadr (ssnamex s))
	     (setq o (vlax-ename->vla-object e))
	     (if (and (vlax-property-available-p o 'textstring))
	       (and (wcmatch (strcase (vla-get-textstring o)) (strcat "*" (strcase f) "*"))
		    (_zoom o ao 2)
	       )
	       (foreach	a (vlax-invoke o 'getattributes)
		 (and (wcmatch (strcase (vla-get-textstring a)) (strcat "*" (strcase f) "*"))
		      (_zoom a ao 2)
		 )
	       )
	     )
	   )
    )
  )
  (princ)
)

just why not...

0 Likes
Message 29 of 37

ronjonp
Mentor
Mentor

@ВeekeeCZ wrote:

@ronjonp wrote:
...

just why not...


Should work nicely 🍻

0 Likes
Message 30 of 37

ronjonp
Mentor
Mentor
Accepted solution

Here's another version that will search tables too. Could not figure out how to zoom to the exact text within, but will zoom to the table.

(defun c:foo (/ _zoom ao bk f o on s)
  ;; RJP » 2020-01-28
  ;; Zoom to *text, mleader and table ( zooms to entire table not text within table )
  (defun _zoom (o ao m / ll pts ur)
    (vla-getboundingbox o 'll 'ur)
    (setq pts (mapcar 'vlax-safearray->list (list ll ur)))
    (vlax-invoke
      ao
      'zoomcenter
      (mapcar '/ (mapcar '+ (car pts) (cadr pts)) '(2 2 2))
      (* m (distance (car pts) (cadr pts)))
    )
    (while (null (getpoint "\nPick a point to find next value: ")))
  )
  (if (and (setq f (getstring t "\nText to find: "))
	   (setq s (ssget "_X"
			  (list	'(-4 . "<OR")
				'(-4 . "<AND")
				'(0 . "insert")
				'(66 . 1)
				'(-4 . "AND>")
				'(0 . "acad_table,*text,multileader")
				'(-4 . "OR>")
				(cons 410
				      (if (= (getvar 'cvport) 1)
					(getvar 'ctab)
					"Model"
				      )
				)
			  )
		   )
	   )
      )
    (progn (setq bk (vla-get-blocks (vla-get-activedocument (setq ao (vlax-get-acad-object)))))
	   (setq f (strcat "*" (strcase f) "*"))
	   (foreach e (mapcar 'cadr (ssnamex s))
	     (setq on (vla-get-objectname (setq o (vlax-ename->vla-object e))))
	     (cond ((vlax-property-available-p o 'textstring)
		    (and (wcmatch (strcase (vla-get-textstring o)) f) (_zoom o ao 2))
		   )
		   ((= "AcDbBlockReference" on)
		    (foreach a (vlax-invoke o 'getattributes)
		      (and (wcmatch (strcase (vla-get-textstring a)) f) (_zoom a ao 2))
		    )
		   )
		   ((= "AcDbTable" on)
		    (vlax-for a	(vla-item bk (cdr (assoc 2 (entget e))))
		      (and (vlax-property-available-p a 'textstring)
			   (wcmatch (strcase (vla-get-textstring a)) f)
			   (_zoom o ao 1)
		      )
		    )
		   )
	     )
	   )
    )
  )
  (princ)
)
0 Likes
Message 31 of 37

Sea-Haven
Mentor
Mentor

Found the way to get a cell xyz etc as you need to use row-column to find text. Took a little while used dumpit looked at methods.

 

(vlax-safearray->list (vlax-variant-value (vla-GetCellextents MyTable row column :vlax-false))) not sure about the false true setting. Seems to work for any cell.screenshot150.png

 

Returns a list of points so nice thing is the zoom point is 1/2 pt2-pt3

 

(setq pt3  (mapcar '+ pt1 pt2) )    adds
(setq pt3 (mapcar '(lambda (x) (/ x 2.0)) pt3)    ; divide by 2

 

 

0 Likes
Message 32 of 37

ВeekeeCZ
Consultant
Consultant

@john.uhden wrote:
It's a habit of mine. Most often when you build a command function that
does not include a (command ...) or (vl-cmdf ...) call then the routine
closes out with some kinda superfluous message. Maybe it's just with a
cancel; I dunno, but it happens. You don't need it if you invoke a
command, but it doesn't hurt to leave it in.

 

Yes, this still happens... I never noticed this before until very recently when I was rewriting some old "Info" routine. Originally with some (command), now without...

This routine prints some info to the command-line - it uses all 3 lines of my docked command-line which I have adjusted to. And now, what a surprise!, when I "improved" my routine to get rid of (command) I could not see my top info line because the last line was always occupied by some Command: !!!

 

Compare:

(defun c:Test1 ()
  (princ "\n1\n2\n3")
  (princ)
  )


(defun c:Test2 (/ pnt1 pnt2)
  (setvar 'cmdecho 0) (command "_.expert" (getvar "expert")) (setvar 'cmdecho 1)
  (princ "\n1\n2\n3")
  (princ)
  )

 

image.png

 

0 Likes
Message 33 of 37

Kent1Cooper
Consultant
Consultant

That is peculiar, but for what it's worth, I find the "dummy command" part can be simpler [dumber?] and have the same result [in barest-minimum trial] -- no need to set an arbitrary System Variable to what it's already set to:

(defun c:Test2 (/ pnt1 pnt2)
  (setvar 'cmdecho 0) (command) (setvar 'cmdecho 1)
  (princ "\n1\n2\n3")
  (princ)
  )

 

Kent Cooper, AIA
0 Likes
Message 34 of 37

ВeekeeCZ
Consultant
Consultant

(command) leaves irrepressible *Cancel*. Just saying...

0 Likes
Message 35 of 37

Kent1Cooper
Consultant
Consultant

@ВeekeeCZ wrote:

(command) leaves irrepressible *Cancel*. Just saying...


Yes, but [I also typically have 3 command lines showing, but I expanded to 5 to illustrate] it's back before the other stuff returned:

test2.JPG

With 3 lines showing, it would be gone from view, so I personally wouldn't care about it.

Kent Cooper, AIA
0 Likes
Message 36 of 37

AKeyM
Participant
Participant

Thanks for this! But I'm wondering if there is a way to narrow the search to an exact match?

Where you write "

"\Text content to find {entire or partial, case-sensitive}: "

This means that if I'm searching, for example, the text ABC, it will find and replace it even if the match is already in another text like ABCD. Is there a way to get only exact matches with the string I'm looking for?

0 Likes
Message 37 of 37

devitg
Advisor
Advisor

@AKeyM 

 

Change this 

(setq txtss (ssget "_X" (list (cons 1 (strcat "*" txtstr "*")) (cons 410 (getvar 'ctab)))))

 

To 

 

(setq txtss (ssget "_X" (list (cons 1  txtstr ) (cons 410 (getvar 'ctab)))))

 

The asterixis is the wildcard  to any character in a TEXT .  

 

0 Likes