Jump to content

Output to command line during code execution


thatandyward

Recommended Posts

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.

Link to comment
Share on other sites

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. :-)

Link to comment
Share on other sites

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

 

>

>>

>>

>>>

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 :x

 

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

628443scratch.gif

 

I'm baffled.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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)
)

2017-11-21_12-39-07.gif

Edited by ronjonp
Link to comment
Share on other sites

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!

Link to comment
Share on other sites

  • 4 years later...

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. 

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...