thatandyward Posted November 15, 2017 Posted November 15, 2017 I have a block processing routine which can take several seconds to complete and I want to provide feedback to the user whilst it does it's thing. acet-ui-progress works great for users with Express Tools loaded, but my lisp routine also needs to work with AutoCAD for Mac users, who don't have Express Tools/ObjectARX. For users without Express Tools I would like to output a simple "Processing Selection..." message to the command prompt before the code starts to process the selection set. I test for "acetutil.arx" in arx and set a flag, which allows me to do a simple IF statement when I invoke the progress bar (see relevant sections of code below). ;; code to initialize error handling, save/change sysvars etc… ;; test if express tools loaded, set Progress bar flag accordingly (if (member "acetutil.arx" (arx)) (setq Pb T) (setq Pb nil) ) ;; …code to initialize output file & other user input/options… ;; prompt user for selection, filter blocks only (if (not (setq Sel (ssget '((0 . "INSERT"))))) (progn ;; no blocks in selection (prompt "No blocks selected") (vl-exit-with-value nil) ) ) ;; initialize index counter (setq Idx (sslength Sel)) ;; invoke progress bar (express tools only) (if Pb (acet-ui-progress "Processing Selection:" Idx) (Prompt "Processing Selection...") ;; output text to prompt if no express tools ) ;; loop all blocks in selection set (repeat Idx (setq Idx (1- Idx)) ;; update progress bar (express tools only) (if Pb (acet-ui-progress Idx)) ;; …code to process block… ) ;; END repeat ;; terminate progress bar (express tools only) (if Pb (acet-ui-progress)) ;; …code to close output file & provide feedback to user my issues is that (Prompt "Processing Selection...") doesn't actually output to the command prompt until the code has finished executing. I wrote a scratch routine to demonstrate the issue (defun C:Scratch ( / Idx ) (setq Idx -1) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (repeat 1000000 (setq Idx (+ 1 Idx))) (terpri) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) When I run the scratch routine nothing gets outputted to the command prompt until the code execution is complete; however I can see from (getvar "MILLISECS") that they are processed pre & post loop respectively, but for some reason the actual output to the prompt is held until the end. If I add in a dummy user input, then the Pre Loop message is outputted as expected. (defun C:Scratch ( / DummyInput Idx ) (setq Idx -1) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (initget "Y N") (setq DummyInput (getkword "\nDummy Request for User Input [Y/N]")) (repeat 1000000 (setq Idx (+ 1 Idx))) (terpri) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) ;; END defun I assume there is some kind of events hierachy at play here, where the code execution is taking precedence over outputting to the command prompt. is there anyway to override that & force the output? I have seen this post, and tried all the suggestions, none of which work, and I'd like to avoid using grtext, which was the 'solution' from this thread. Quote
thatandyward Posted November 16, 2017 Author Posted November 16, 2017 Thanks for the suggestion - no dice I'm afraid. Have tried Princ, Print & Prin1 as well as Prompt, same behavior from all of them Quote
Aftertouch Posted November 16, 2017 Posted November 16, 2017 Im At the office now and had a better look at the code. I notice that you first execute the repeat function, and call the 'prompt' afterwards. In the code below i included the prompt within the repeat function. (defun C:Scratch ( / DummyInput Idx ) (setq Idx -1) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (initget "Y N") (setq DummyInput (getkword "\nDummy Request for User Input [Y/N]")) (repeat 1000000 (terpri) (setq Idx (+ 1 Idx)) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) ) (princ) ) ;; END defun Just some bracket swapping. :-) Quote
BIGAL Posted November 16, 2017 Posted November 16, 2017 My $0.05 you will see something happening. (setq Idx (1- Idx)) ; (command "txtscr") try this also (princ (strcat "\n" (rtos Idx 2 0))) You could write a DCL that just flashes up using delay before disappearing say something like this just with a heading only as text no input > >> >> >>> Quote
thatandyward Posted November 16, 2017 Author Posted November 16, 2017 Thanks for the suggestions both. Just some bracket swapping. :-) &Aftertouch my issue is that the first Prompt function (called before the repeat function) doesn't display on the command line until after all the code has finished executing. moving the second Prompt function inside the repeat loop doesn't affect that, and the repeating prompt functions are also not actually displayed on the command line until the code completes. (setq Idx (1- Idx)) ; (command "txtscr") try this also (princ (strcat "\n" (rtos Idx 2 0))) You could write a DCL that just flashes up using delay before disappearing say something like this just with a heading only as text no input > >> >> >>> @BIGAL I have experimented with the (textscr) function but not tried it as (command "textscr"); however in both instances it causes the command line to open as a new window, which seems a bit obnoxious for a script to impose on a user. (textscr) causes the command line window to open & switch focus before the loop, but the pre-loop prompt function output still doesn't display until after the code completes. Interestingly when I use (command "textscr"), the command line window opens/switches focus and also displays "textscr" on the command line before the loop starts, but again the pre-loop prompt still doesn't display until after the code has completed. I have played with using a DCL, which does display at the correct time; however DCL does not work for AutoCAD for Mac, which is the scenario I'm trying to solve with the prompt output as an alternate method of feedback for those without express tools /acet-ui-progress still scratching my head on this one Quote
thatandyward Posted November 16, 2017 Author Posted November 16, 2017 Have a read HERE. Thanks Ron, yes I had seen that and have tried using both (command "_.delay" 0) and MP:Echo. I tried calling (MP:Echo nil) (like a DoEvents function) and also tried (MP:Echo (strcat "Pre Loop " (itoa (getvar "MILLISECS")))). neither option for MP:Echo or (command "_.delay" 0) had the desired effect, the pre-loop message still didn't appear until the code execution completed. interestingly, using _.delay is another, perhaps simpler, way to demonstrate the issue (defun C:Scratch ( / ) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (terpri) (command "_.delay" 2000) ;; delay for 2s (terpri) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) ;; END defun Quote
Jef! Posted November 16, 2017 Posted November 16, 2017 That is the kind of thing that makes me go "wthhhhhh autodesk?!?!?". Maybe time to upgrade to windows xp 32 bits with Acad R12 or something The only workaround I found to that problem works like 75% of the time. The rest of the time, CAD freeze for up to few seconds, which is most of the time enough to have the routine complete its task and give feedback when back to "idle"...but for longer execution lisp, I had more acceptable feedback results. (Making the delay 15 secs, Cad may freeze for 5 or 6 secs) here'S my workaround (defun C:Scratch ( / ) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) ;(ACET-UI-PROGRESS-DONE);that added decrease further the chance of freeze ; ^ but add a ± 0.2 sec delay before the preloop prompt appear (terpri) (Jef!:wait 2) ;; delay for 2s (terpri) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) (defun Jef!:wait (seconds / stop) (setq stop (+ (getvar "DATE") (/ seconds 86400.0))) (while (> stop (getvar "DATE")) (grtext)) ) I'm not sure if it would work on Mac, as despite grtext is supported for MacOS, the (2015) grtext help has this note: "This function is supported on Mac OS, but does not affect AutoCAD.", so... tell me if it works! Cheers Quote
thatandyward Posted November 17, 2017 Author Posted November 17, 2017 This didn't work for me unfortunately, same result of the pre-loop message not displaying until after the code execution completes. I was playing around with grread this afternoon, using a similar approach inside a while loop (defun C:Scratch ( / t1 t2 ) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (terpri) (setq t1 (getvar "MILLISECS") t2 (+ t1 1000) ) (while (> t2 (getvar "MILLISECS")) (grread T 2) ) (command "_.delay" 2000) ;; delay for 2s (terpri) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) ;; END defun This causes the pre-loop message to display straight away, unfortunately the code execution then halts until grread gets some form of user input such as a keystroke or mouse movement. Chances are the user will have their hand on the mouse but it feels too much of a risk, if they don't then it could cause a much longer delay, which feels like a bad trade-off for a feedback message. Whilst playing around I also looked at what happens when the alert function is added to the mix after the pre-loop prompt function. Similar to grread this also causes the pre-loop message to display straight away, but obviously alert halts code execution until it's dismissed by the user, so that's doesn't help. I did wonder if setting bit 2 (4) of the QAFlags system variable (no "alert" dialogs, text display instead.) would work in my favor, but alas when set the pre-loop message reverted back to not displaying until the code completes, which makes sense as there is no longer a pause for user input. Pausing for user input clearly flushes the event stack and allows the pre-loop message to display, but then the problem is how do you dismiss the user input request without user intervention? I suspect that's likely not possible as it kind of goes against the fundamentals of pausing for user input. sigh. Quote
BIGAL Posted November 17, 2017 Posted November 17, 2017 Had a bit of a think more about this nearly every one has the command line shown say 2-3 lines so as an example (setq x ">") (repeat 50 (princ (strcat "\n" (setq x (strcat X ">"))))) (command "txtscr") ; just to see as this is to fast. You can take your number of items and use some form of ratio before it adds another > say 4000 items divide by max number say 100 for across the command line so inc =40 so every 40 items it adds a > make a little defun y=y+1 if 41 do +> y=1 again and so on. Quote
thatandyward Posted November 17, 2017 Author Posted November 17, 2017 Had a bit of a think more about this nearly every one has the command line shown say 2-3 lines so as an example (setq x ">") (repeat 50 (princ (strcat "\n" (setq x (strcat X ">"))))) (command "txtscr") ; just to see as this is to fast. You can take your number of items and use some form of ratio before it adds another > say 4000 items divide by max number say 100 for across the command line so inc =40 so every 40 items it adds a > make a little defun y=y+1 if 41 do +> y=1 again and so on. @BIGAL - I think you misunderstand the issue I'm having - I'm not trying to re-create a progress bar on the command line. My issue is that any prompt/princ/print/prin1 functions do not display until either, there is a pause for user input or the entire code is executed. I do appreciate you spending time thinking about the issue though Quote
Jef! Posted November 17, 2017 Posted November 17, 2017 This didn't work for me unfortunately It is funny how execution/results/behaviors can vary from a version of cad to another version of cad, or sometimes just from 1 pc to another. But now my cad just stepped into x-file behavior. I copied my previously posted code to try another approach. Paste in CAD. Keep trying Scratch. Doesn't work. Same as you. I then notice that I have Another Cad session, the one from yesterday. In that session, it works. Scrolling back up, the code is the exact same. Left side, todays. Right side Yesterdays. I'm baffled. Quote
thatandyward Posted November 17, 2017 Author Posted November 17, 2017 How strange. Can you think of anything that might be loaded/in-effect in yesterdays session but not today's? That might be a clue to what's preventing the output to the command line to display Quote
Jef! Posted November 17, 2017 Posted November 17, 2017 How strange. Can you think of anything that might be loaded/in-effect in yesterdays session but not today's? That might be a clue to what's preventing the output to the command line to display Well I tend to open 1 cad instance to run vlide in (lets name it A), another for the dwg to test new code in (lets name it B), That way if by mistake I create an endless loops, "end task" in B wont kill my vlide in session A. I open more instances to do whatever else. I stick to 1 drawing/different use per cad session, so if 1 crash it doesnt close anything other than the dwg that crashed. Most of the time my cad with vlide session can be up for many days. Yesterday's try, was on a newly started session ©. Today's (no feedback until completion) try was on the dwg cad session (B) that have been up and running for over a week or 2. I just tried on my vlide session (A) which has been up and running for 2 weeks, but only used only for vlide. No feedback until completion either. Trying the code on a new session (D) again work most of the time, feedback in real time, or slight delay. The culprit here seems to be 100% related to long uptime. Long uptime has a tendency to hog cpu and increase the % usage, which is unnoticeable with multi core processors, but definitely has an impact on this specific scenario... and to think of it, I was rewriting a function I made long time ago, with a lot more efficiency. I wrote few different versions, benchmarking each new version vs the older every time I finished a new one. I was wondering why the benchmark time of my base line (original function) increased. Quote
thatandyward Posted November 20, 2017 Author Posted November 20, 2017 Hmm interesting. so possibly some kind of resource prioritization in effect? My testing has all been in sessions with relatively short uptime (a few hours/less than a day) and I just tried it again with a fresh re-booted OS & AutoCAD session and still no joy. I am on a virtualized environment (Parallels/Windows 10) for my Windows AutoCAD 2018 set-up so can determine how many processors/memory etc to allocate to the system. My default config allocates 4 processors, 8GB of RAM & 512MB of graphics memory, so should be more than enough for this simple scratch routine. I tried playing around with those settings this morning (cranking right up & right down), nothing had any noticeable affect on the outcome. Also get the same results when testing in AutoCAD 2016 for Mac, which is running natively on OSX, so in theory making full use of all system resources and again freshly booted OS/AutoCAD session. I'm getting the feeling that I'm not going to win this one and might have to live with a less-than-desirable UI/feedback experience for my AutoCAD for Mac users. sigh. Appreciate all the help & feedback offered by all. Quote
ronjonp Posted November 21, 2017 Posted November 21, 2017 (edited) It's not pretty, but this seems to work for me: (defun c:scratch (/ idx) (setq idx -1) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (setvar 'cmdecho 0) (repeat 1000000 ;; Print every 500 iterations (if (= 0 (rem (setq idx (+ 1 idx)) 500)) (progn (prin1 (strcat "\nProcessing: " (itoa idx))) (command "_.delay" 0)) ) ) (setvar 'cmdecho 1) (terpri) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) Edited November 21, 2017 by ronjonp Quote
thatandyward Posted November 22, 2017 Author Posted November 22, 2017 Thanks Ron, very enlightening. I had previously tried using (command "_.delay" 0) as a kind of 'DoEvents' function without any success; however your routine worked for me. I'm not particularly looking to output to the command line during the loop, just trying to get the initial message to display before the loop executes. So I tried adding (command "_.delay" 0) after the initial pre-loop loop message and removed the princ/(command "_.delay" 0) functions inside the loop and it no longer worked. (defun C:Scratch ( / Idx ) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (terpri) (setvar 'CmdEcho 0) (command "_.delay" 0) (setvar 'CmdEcho 1) ;; loop to simulate code execution time (setq Idx 0) (while (< Idx 5000000) (setq Idx (+ 1 Idx))) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) ;; END defun note: I reverted back to using a loop/counter to simulate the code execution time, to avoid confusion from utilizing the delay function for two different purposes. It then occurred to me that the reason your version was working might be related to the number of times (command "_.delay" 0) is being called. (defun C:Scratch ( / Idx ) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (terpri) (setvar 'CmdEcho 0) (repeat 50 (command "_.delay" 0)) (setvar 'CmdEcho 1) ;; loop to simulate code execution time (setq Idx 0) (while (< Idx 5000000) (setq Idx (+ 1 Idx))) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) ;; END defun Bingo. It worked. I experimented with the number of times to repeat the delay function, the higher the number the more reliable it was. At 10 it would work occasionally, at 25 it would work most of the time and at 50 it appears to work all the time. My theory is that the command line events stack is de-prioritized during code execution but it gets flushed once there are a certain number of items in the stack. I wrote a small function which flushes the command line stack that I call after the pre-loop message which appears to working. I upped the number of times it repeats to 65 in case the max stack count is base-2 i.e. 64 (total guess). (defun C:Scratch ( / Idx ) (prompt (strcat "Pre Loop " (itoa (getvar "MILLISECS")))) (terpri) (FlushCmdLnStack) ;; loop to simulate code execution time (setq Idx 0) (while (< Idx 5000000) (setq Idx (+ 1 Idx))) (prompt (strcat "Post Loop " (itoa (getvar "MILLISECS")))) (princ) ) ;; END defun (defun FlushCmdLnStack ( / CmdEcho) (setq CmdEcho (getvar 'CmdEcho)) (setvar 'CmdEcho 0) (repeat 65 (command "_.delay" 0)) (setvar 'CmdEcho CmdEcho) ) ;; END defun Not sure the exact reason why this works, but it appears to solve my issue and I'm glad to have finally solved it. Thanks again to all who helped with this one! Quote
jameslitvak Posted January 5, 2022 Posted January 5, 2022 So I realize this is a very old thread, but I stumbled onto it while searching for something else, and wanted to add a solution. Instead of adding all kinds of extra tasks or commands, a simple (princ) makes it work. @thatandywardI'm not quite sure why you put the (terpri) lines in there, but they're doing nothing in the code. Here's how I got the pre-loop message to be displayed, using the version you shared above with the delay simulating events happening. (defun C:Scratch () (prompt (strcat "Pre Loop " (itoa (getvar 'MILLISECS)))) (princ) (command "_.delay" 2000) ;; delay for 2s (prompt (strcat "Post Loop " (itoa (getvar 'MILLISECS)))) (princ) ) ;; END defun I'm not sure why the extra (princ) forces the pre-loop message to display, but it works. Quote
Recommended Posts
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.