I have a routine that requires multiple user inputs to create a viewport, zoom to a desired location, then write some annotation based on the contents of the veiwport. Currently my routine is very fragile. If the user missclicks at any point they need to cancel the routine and restart it to fix their error.
I want to add the ability for the user to press a key and step backwards through the routine. It will need to undo the most recent set of actions and take the user back to the corresponding input request.
I have tried making a function (CHK) that checks if an input is valid and if not calls the undo function. I then wrapped every user input request in the CHK function. This is clunky, inconsistent and doesn't send the user back to redo the input.
Is there a better way to get this to work? Am I missing some basic functionality of the undo command in LISP? Is there a way to create a GOTO function?
Solved! Go to Solution.
It might be a draw out way of doing it but could nested loops work for this?
On an undo command the current loop can undo its actions, break and send the user up one level of loop.
Something like this
[Loop A (does action X) (if undo command, undo actions)
[Loop B (does action Y) (if undo command, undo actions and reset to Loop A)]
] ;end of Loop A
To get the best advice on how to handle the user interaction, you should post code, so that we know what the expected input is. One way you could handle user input is like this:
(while (not input)
(setq input (GetUserInput))
(if (/= input expectedInput) (setq input nil))
)
There is sort of pseudo code below.
But first I would say not to take any AutoCAD action until all your inputs are acceptable.
Then divide each "action" of yours into functions that be called at any appropriate time based on conditions.
F'rinstance you might start with a function called "start" and then "next1" "next2" etc. each of which, if successful, creates non-nil variables we'll call "started" "done1" "done2" etc.
Keep a local variable named "done" which with zero effort starts out as nil because it's local.
Then,
(while (not done)
(cond
((not started)(start))
((not done1)(next1))
((not done2)(next2))
;; etc.
(1 (setq done 1))
)
;; and carry on.
That's the way my feeble mind has worked for decades, and <checking my pulse> I'm still here.
John F. Uhden
1. Where is the code?
2.No loops here, 'while' functions, like they said.
3. Probably more 'undo' arguments could be used, in your the putative 'code.'
4. We used the following one key, no enter required, subroutine because designers liked the computer to do the work.
The default value can be changed to the last answer, if appropriate.
; input yes no AusCadd.com, SCG
(Defun I_YN (QSTR YNFLG / TF NF IT IG IK ) ;
(princ (strcat QSTR (if YNFLG " N or < Y > " " Y or < N > " )) )
(while (and (setq IT (car (setq IG (grread T )) )) (/= 6 IT )
(setq IK (cadr IG )) ; key maybe
(not (and (= 2 IT ) (or ; keyboard
(setq NF (or (= 110 IK ) (= 78 IK )) )
(setq TF (or (= 121 IK ) (= 89 IK )) )
(= 13 IK ) (= 32 IK )) ) )
(not (= IT 11 ))) ) ; end while ; mou R
(setq YNFLG (cond (NF nil ) (TF t ) (t YNFLG )) )
(princ (if YNFLG " Y " " N " )) YNFLG )
The initget function comes to mind does not allow a not correct answer I use a Library dcl for this type of question, you can only pick a correct answer, like John also a series of defuns, you cans use the "TYPE" function for must be a number not a "x" and other methods.
@Anonymous ,
You definitely need to look into the (initget ...) function.
Otherwise, here's my example of maybe how you could approach it:
(defun c:TEST ( / notDone back cnt pt ans number) (setq notDone t cnt 0) (while notDone (setq back nil) (cond ((zerop cnt) (initget 1) (setq pt (getpoint "\nYou MUST select a point: ")) );cond 1 ((= 1 cnt) (initget "Back Forward") (setq back (eq "Back" (setq ans (getkword "\nBack or Forward? [Back/Forward]")))) );cond 2 ((= 2 cnt) (initget 7 "Back") (setq back (eq 'STR (type (setq number (getint "\nEnter a positive, non-zero integer, or..[Back]: "))))) );cond 3 );cond (setq cnt ((if back 1- 1+) cnt)) (setq notDone (not (< 3 cnt))) );while (alert (apply 'strcat (list "You answered the following: " "\nPoint: (" (rtos (car pt)) " " (rtos (cadr pt)) " " (rtos (caddr pt)) ")" "\nBack or Forward: " ans "\nNumber: " (itoa number) );list );apply );alert (prompt "\nTEST Complete.") (princ) );defun
Best,
~DD
@CodeDing , @Anonymous
I should have mentioned this earlier.
If you really want to create/modify objects along the way (maybe for visual purposes), but be able to step backwards, then vla-endundomark, vla-startundomark, <do things>, and vla-endundo mark, which will provide for 1 single "U" for each group of "things."
John F. Uhden
Thank you all for these options. There is a lot for me to go over here.
After reading through them I think a combination of what @CodeDing ,@john.uhden and @Anonymous said will work. Calling in predefined functions will make it easier for me to visualize where the steps are, the initget function will allow me to only accept correct inputs, adding the [Back] to the prompts will give me a better way of initializing the step back and grouping the actions with vla-startundomakr will make it easier to step backwards through the actions. I had already tried vla-startundomark to group the actions but thought I needed overlapping sets of grouped actions and it wasn't working.
Thanks again for all your ideas. Hopefully I can get this to work now.
@Anonymous wrote:
.... grouping the actions with vla-startundomakr will make it easier to step backwards through the actions. I had already tried vla-startundomark to group the actions but thought I needed overlapping sets of grouped actions and it wasn't working. ....
You may be able to make that kind of thing work using Undo Mark and Undo Back, rather than [or maybe in combination with] (vla-startundomark) and (vla-endundomark), which are equivalent to Undo BEgin and Undo End. But it seems you must use (command "_.undo" ...) functions for the Mark and Back options -- there don't seem to be (vla-) functions for them. See >this<.
For anyone that is still interested I have got this to work. The work around of defining every step as a function and then calling each function works wonders. What I have ended up with is a recursive loop that calls the relevant function when conditions are met.
I have defined functions A, B, C, D, and E. If everything runs smoothly A calls B which calls C and so on. Each function has vla-startundomark / vla-endundomark wrapping it so it can be easily undone.
If the user wants to step backwards I have used initget to define the string "Back" as an acceptable input.
If function C receives "Back" as an input it undoes the last step then calls B. This allows the user to step backwards through the routine and fix bad inputs.
Thank you all for your ideas. They helped me get my head around the problem and get a working solution.
John F. Uhden
Can't find what you're looking for? Ask the community or share your knowledge.