Jump to content

Lisp - Prompt user input to select entity but also for keyword


kokjoo89

Recommended Posts

I want to write some lisp routines that similar to autocad prompting option, for example "offset" command, when the command prompt input for offset distance it also can receive other type of input "through/erase/layer". Or when it prompt selection of entity, it still can receive "exit/undo" keyword.

 

How to write a lisp program similar to that?:?

 

Thank in advanced.

Link to comment
Share on other sites

Thank you, Mr. Lee Mac. You are really amazing.

I learn a lot from your tutorial. :notworthy: and i will keep learning from you.

Link to comment
Share on other sites

(setq ent_lst nil ent0 nil)
(while (= ent0 nil)
(initget "Distance")
(setq ans (entsel "\nSelect rectangle to chamfer [Distance]: "))
(if (= ans "Distance")
	(progn
	(setq cdis (getreal "Chamfer Distance: <1.0>"))
	)
	(progn
	(setq ent0 ans)
	(setq ent_lst (entget(car ent0)))
	)
)
)
(if (= cdis nil) (setq cdis 1.0))

This is part of the code i done to prompt for select entity(rectangle) also option to change chamfer distance.

Chamfer distance have a default value of 1.0, if i want the value display 1.0 by default but if user previously already input a new value it display that value instead.

Is this possible?

Link to comment
Share on other sites

Thank you, Mr. Lee Mac. You are really amazing.

I learn a lot from your tutorial. :notworthy: and i will keep learning from you.

 

You are too kind kokjoo89 - I'm pleased that you could learn from my tutorial :)

 

(setq ent_lst nil ent0 nil)
(while (= ent0 nil)
(initget "Distance")
(setq ans (entsel "\nSelect rectangle to chamfer [Distance]: "))
(if (= ans "Distance")
	(progn
	(setq cdis (getreal "Chamfer Distance: <1.0>"))
	)
	(progn
	(setq ent0 ans)
	(setq ent_lst (entget(car ent0)))
	)
)
)
(if (= cdis nil) (setq cdis 1.0))

This is part of the code i done to prompt for select entity(rectangle) also option to change chamfer distance.

Chamfer distance have a default value of 1.0, if i want the value display 1.0 by default but if user previously already input a new value it display that value instead.

Is this possible?

 

Your code is a good start - well done :thumbsup:

 

Personally, I prefer to use the following construction:

(defun c:test ( / ans dis ent enx ) ;; Define function & declare local variables ('dis-global' remains a global variable)
   ;; Initialise global variable if null
   (if (null dis-global)
       (setq dis-global 1.0)
   )
   (while ;; While the following expression returns a non-nil value
       (progn ;; Evaluate the following expressions and return the result of the last evaluated expression
           (setvar 'errno 0) ;; Reset the ERRNO system variable (this sys var does not automatically reset)
           (initget "Distance") ;; Initialise the keyword for the next user prompt
           (setq ans (entsel "\nSelect rectangle to chamfer [Distance]: ")) ;; Prompt the user for a selection or keyword
           (cond
               (   (= 7 (getvar 'errno)) ;; Missed pick
                   (princ "\nMissed, try again.") ;; Notify user & stay in loop
               )
               (   (null ans) ;; User pressed Enter
                   nil ;; Exit loop
               )
               (   (= "Distance" ans) ;; User entered keyword
                   (if (setq dis (getdist (strcat "\nChamfer distance <" (rtos dis-global) ">: "))) ;; Prompt user for chamfer distance
                       (setq dis-global dis) ;; Update global variable if user enters a new value
                       (setq dis dis-global) ;; Else use global variable value
                   ) ;; end if
               ) ;; end condition
           ) ;; end cond
       ) ;; end progn
   ) ;; end while
   (if ans ;; If an object was selected
       (progn ;; Evaluate the following expressions and return the result of the last evaluated expression
           (setq ent (car ans) ;; Obtain the selected entity name
                 enx (entget ent) ;; Obtain the DXF data for the selected entity
           ) ;; end setq
           (princ ;; Print the entity type & chamfer distance for the selected object (just as an example)
               (strcat ;; Concatenate the following strings
                   "\nUser selected a " (cdr (assoc 0 enx)) 
                   " with a chamfer distance of " (rtos dis)
               ) ;; end strcat
           ) ;; end princ
       ) ;; end progn
       (princ "\nUser did not select an object.") ;; Else the user did not select anything
   ) ;; end if
   (princ) ;; Suppress the return of the last evaluated expression
) ;; end defun

(Please note that the above is untested!)

 

In my opinion, by using the (while (progn)) structure, it is easier to control the loop and avoids the need for 'flag' variables.

 

This also allows for missed selection picks and allows the user to exit by pressing ENTER at the prompt.

Link to comment
Share on other sites

:notworthy:Thank again for the amazing code, 100x better than what i had done for whole day. I will study your code to improve my lisp.

Below are my long,messy,inefficient lisp program that i spend whole day try-in-error to get it. It not pretty but get the job done.:P

 

Mainly the program is prompt for user select a rectangle then offset the rectangle to inner, chamfer all corner of selected rectangle. after that create 4 line joining the corner to chamfer. offset and chamfer distance are same, and it can change "Distance".

(defun C:PLA ()

(setq osmd (getvar "osmode"))
(setq ocmd (getvar "cmdecho"))
(graphscr)
(setvar "osmode" 0)
(setvar "cmdecho" 0)
(setvar "plinetype" 2)
(command "UCS" "world");reset UCS to "World"

(setq ent_lst nil ent0 nil)
(while (= ent0 nil)
	(initget "Distance")
	(setq ans (entsel "\nSelect rectangle to chamfer [Distance]: "))
	(if (= ans "Distance")
		(progn
		(setq cdis (getreal "Chamfer Distance: <1.0>"))
		)
		(progn
		(setq ent0 ans)
		(setq ent_lst (entget(car ent0)))
		)
	)
)
(if (= cdis nil) (setq cdis 1.0))

(setq olyr (getvar "clayer"))
(setvar "clayer" (cdr(assoc '8 ent_lst)))

(if (= (cdr(assoc 0 ent_lst)) "LWPOLYLINE")
(progn

(setq len (length ent_lst))
(setq n 0)
(setq vernum 1 verlst nil)

(repeat len 
	(setq ent_tmp (car(nth n ent_lst)))
	(if (= ent_tmp 10)
		(progn
		(terpri)
		;(princ (cdr(nth n ent_lst)))
		(setq verlst (cons (cons vernum (cdr(nth n ent_lst))) verlst))
		(setq vernum (1+ vernum)) 
		)
	)
	(setq n (1+ n))
)

(if (= (length verlst) 4)
	(progn
	(setvar "chamfera" cdis) 
	(setvar "chamferb" cdis)
	(setq midp_x (/(+ (cadr(assoc '1 verlst)) (cadr(assoc '3 verlst))) 2))
	(setq midp_y (/(+ (caddr(assoc '1 verlst)) (caddr(assoc '3 verlst))) 2))
	(setq midp (list midp_x midp_y))

	(command "offset" cdis ent0 midp "")

	(command "chamfer" "p" ent0)
	
	(if (< (cadr(assoc '1 verlst)) (cadr(assoc '3 verlst)))
		(setq pt_x (cadr(assoc '1 verlst)))
		(setq pt_x (cadr(assoc '3 verlst)))
	);get ori point_x to start line
	(if (< (caddr(assoc '1 verlst)) (caddr(assoc '3 verlst)))
		(setq pt_y (caddr(assoc '1 verlst)))
		(setq pt_y (caddr(assoc '3 verlst)))
	);get ori point_y to start line
	(setq pt0 (list (+ pt_x cdis) (+ pt_y cdis)))
	(setq ldis (/(sqrt (* (* cdis cdis) 2)) 2))
	(setq pt1 (polar pt0 (* 1.25 PI ) ldis))

	(command "line" pt1 pt0 "")
	
	(setq mid_ymp (list midp_x (1+ midp_y)))
	(setq mid_xmp (list (1+ midp_x) midp_y))
	(setq ent_l (entlast))
	(command "mirror" ent_l "" midp mid_ymp "N")
	(setq ent_2 (entlast))
	(command "mirror" ent_l ent_2 "" midp mid_xmp "N")
	(princ "\nDone")
	
	(command "UCS" "P");restore previous UCS
	
	)
	(progn
	(terpri)
	(princ "Invalid Rectangle, ")
	(princ "It has ")
	(princ (length verlst))
	(princ " corners")
	)
)
)
(princ "Invalid lwpolyline")
)

(setvar "osmode" osmd)
(setvar "cmdecho" ocmd)
(setvar "clayer" olyr)
(princ)
)

 

Sample of what this program intended to do:

tsHEf1A.png

 

I'm still having problem declare local variable. when i declare some as local variable the program not working. Very confuse. Lot to learn about lisp.:?

 

Some question:

1) I'm not familiar with (while (progn)) structure, all i know is (while ). How (while (progn)) structure work? it can work without flag?

2) what is the difference between (null xxx), (= xxx nil), (= xxx "")?

3) how come (if ans ...) can evaluate "ans", if "ans" is other the "T"/nil? should it need some evaluation expression like (if (= ans xxx))?

 

Again, thank Mr. Lee Mac :notworthy::notworthy::notworthy:

Link to comment
Share on other sites

I'm still having problem declare local variable. when i declare some as local variable the program not working. Very confuse.

 

Where local variable declaration is concerned, the following may help you to gain a better understanding of the concept:

 

Local Variable Scope

Localising Variables

 

Now, in response to your questions:

1) I'm not familiar with (while (progn)) structure, all i know is (while ). How (while (progn)) structure work? it can work without flag?

The progn expression is the test expression in this case (or 'flag' as you refer to it); however, in my example there are no expressions to be evaluated while the test expression is validated.

 

The progn function will merely evaluate the supplied expressions and will return the value returned by the last evaluated expression. Therefore, using this structure, we can evaluate many expressions and control the loop by simply controlling the value returned by the last evaluated expression - this is similar in nature to a do-while expression in other languages.

 

2) what is the difference between (null xxx), (= xxx nil), (= xxx "")?

 

  • null verifies that a symbol or expression evaluates to nil

 

  • = is typically used to compare the absolute equality of numbers, symbols or strings; hence in this case, you are testing whether the value held by the symbol xxx is identical to the symbol nil, which returns true. Although both null & (= nil) would serve the same purpose in this case, semantically, I would use null in this instance.

 

  • (= xxx "") is entirely different to (= xxx nil): one expression is testing whether the value held by the symbol xxx is equal to the symbol nil; the other is testing whether this value is equal to an empty string. Note that an empty string and nil are very different:

_$ (= "" nil)
nil
_$ (< nil "")
T

3) how come (if ans ...) can evaluate "ans", if "ans" is other the "T"/nil? should it need some evaluation expression like (if (= ans xxx))?

In AutoLISP, any non-nil expression can validate the test expression for a conditional function (if / cond / while). That is, providing the symbol or expression is not equal to nil, the test expression will be validated - the symbol or expression need not hold a boolean value.

 

Observe:

_$ (if 1 "yes" "no")
"yes"
_$ (if "a" "yes" "no")
"yes"
_$ (if "" "yes" "no")
"yes"
_$ x
nil
_$ (if x "yes" "no")
"no"
_$ (if 'x "yes" "no")
"yes"

Again, thank Mr. Lee Mac :notworthy::notworthy::notworthy:

 

You're very welcome! :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...