Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

EXITing nested LOOPs 2

Status
Not open for further replies.

ChrisRChamberlain

Programmer
Mar 23, 2000
3,392
GB
I have a program/procedure with the following basic structure.<br><br>SCAN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DO WHILE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FOR<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDFOR<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SCAN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDSCAN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDDO<br>ENDSCAN<br><br>The program is a generic file finder which performs a recursive search on selectable drives for selectable file specifications and outputs the results to a table.<br><br>I would like to introduce a cancel option, preferably by mouse click, to enable a user to abort the search.<br><br>Any ideas please?<br><br>Chris<br>
 
LoopExit = .t.<br>scan while LoopExit<br>&nbsp;&nbsp;do while LoopExit<br>&nbsp;&nbsp;&nbsp;&nbsp;for <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if messagebox(&quot;Continue with For&quot; , 4) = 7<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LoopExit=.f.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endif<br>&nbsp;&nbsp;&nbsp;&nbsp;scan<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if messagebox(&quot;Continue with Scan&quot;,4)=7<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LoopExit=.f.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endif<br>&nbsp;&nbsp;&nbsp;&nbsp;endscan<br>&nbsp;&nbsp;&nbsp;&nbsp;endfor<br>&nbsp;&nbsp;enddo<br>endscan<br><br> <p>David W. Grewe<br><a href=mailto:Dave@internationalbid.net>Dave@internationalbid.net</a><br><a href= > </a><br>
 
Hmmm.... &quot;cancel&quot;&nbsp;&nbsp;you might try adding a timer to the form and moving the loop portion of your search to the timer's trigger (can't rember the method's name). If your loop is is doubled nested you may have to rethink the design.&nbsp;&nbsp;You want 1 loop to make this work correctly.&nbsp;&nbsp;<br><br>You will also have to play with the &quot;interval&quot; property to balance search speed with &quot;cancel&quot; click delay.&nbsp;&nbsp;I have done this with other lengthy routines with good success before.&nbsp;&nbsp;<br><br>One way to speed up the balancing of the interval is to add some code to your search and calculate the average cycle time for your loop.&nbsp;&nbsp;You'll probably want your interval to be about 10%-15% higher than your average cycle time. That will hurt performance but it will let you cancel it too.&nbsp;&nbsp;If you want to be really fancy, calculate the average cycle time in your program, that way it will adjust to each machines performance - just a thought.<br>&nbsp;
 
Chris,<br>I agree with David's solution.<br><br>Just remember that the calling program needs some way of knowing that the user aborted and that the process didnt just complete successfully. Maybe by declaring LoopExit public or passing it by reference to this procedure.<br><br>It is more windows-esk to include a progress bar with a cancel button. However, if you choose this method, you are going to need the DOEVENTS command to process windows events while processing VFP code. Use DOEVENTS cautiously, for each time it's executed, the processor must stop, check the queue for any pending events, and process any event it finds. This dramatically slows down long processing runs. If you do go this route, only use DOEVENTS every X number of iterations thru the loop, to reducee processing time.<br><br>do while ! lloopexit<br>&nbsp;&nbsp;lnCount = 1 + lnCount<br>&nbsp;&nbsp;IF lnCount&gt;=100<br>&nbsp;&nbsp;&nbsp;&nbsp;DOEVENTS<br>&nbsp;&nbsp;&nbsp;&nbsp;lnCount=0<br>&nbsp;&nbsp;ENDIF<br>&nbsp;&nbsp;*process here<br>enddo<br><br>My preference is using an ON KEY LABEL assignment. I prompt the user with a progress bar and a caption that reads 'Press Ctrl+F1 to Cancel'. I use ON KEY LABEL Ctrl+F1 lLoopExit = .T. and the same basic algorithm as David did.<br><br>Just my two cents.<br><br>Jon
 
David, BlindPete and Jon<br><br>Thanks for the ideas<br><br>David had no way of knowing it but the Do While loop relates to the folder search and thus the MessageBox function is constantly called.<br><br>BlindPetes idea has yet to be tried. Variable lnLoopTime, could be determined but will vary depending on the number of files in any given folder.<br><br>lnLoopTime = 1<br><br>SCAN<br>&nbsp;&nbsp;&nbsp;DO WHILE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnStart = SECONDS() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnExit = INKEY(lnLoopTime * lnFactor,[HM])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF <br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnEnd&nbsp;&nbsp;&nbsp;= SECONDS()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnLoopTime = lnEnd - lnStart<br>&nbsp;&nbsp;&nbsp;ENDDO<br>ENDSCAN<br><br>I tried Jon's idea originally with ESC, (also with LASTKEY() = 27), which would have been the most intuitive key, without success. <br><br>His key combination works but as far as the progress bar is concerned, there is no way of predetermining how many records may be output or how long the processing might take.<br><br>As the program is emulating the Windows file finder, the user would expect a Stop or Cancel button. <br><br>Any further or comments please<br><br>Chris
 
Cant you add a variable to your do while and set it falso with some event?&nbsp;&nbsp;First think I thought of but I admit I didn't read everything here&nbsp;&nbsp;:eek: <p>John Durbin<br><a href=mailto: > </a><br><a href= > </a><br>ICQ VFP ActiveList #73897253
 
David, BlindPete, Jon and John<br><br>The following code works, (most of the time!)<br><br>SCAN<br>&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;ENDIF <br>&nbsp;&nbsp;&nbsp;DO WHILE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnExit = INKEY(.06,[HM])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF <br><br><br>&nbsp;&nbsp;&nbsp;ENDDO<br>ENDSCAN<br><br>The program will crash with Error no 11, &quot;lnExit = INKEY(.06,[HM])&quot;.<br><br>The program code was &quot;Function argument value, type, or count is invalid.&quot;, and the value of the last key pressed was 126.<br><br>Any ideas<br><br>Chris<br>
 
Chris<br>DOEVENTS is definately the way to handle it.<br><br>Thanks Jon and David!<br><br>Every now and then you learn some tid-bit that changes your whole perspective.&nbsp;&nbsp;Thanks guys... I tested doevents on one of my own lengthy loops.&nbsp;&nbsp;I embeded it in a progress bar and used the modulus to fire it every three percent.&nbsp;&nbsp;Very little loss in performance... Very cool and thanks again! <p> <br><a href=mailto:blindpete@mail.com>blindpete@mail.com</a><br><a href= > </a><br>
 
Chris,<br>&nbsp;Personally, I always advise against the Inkey() function if it isnt absolutely necessary. Basically because of this statement in the help file:<br><br><i>If there are several keys in the type-ahead buffer, INKEY( ) returns the value of the <b>first</b> key entered in the buffer.</i><br><br>I take this to mean, anytime you use the Inkey() function, you must process every key in the buffer.Using the DO LOOP from your example, in order to accurately use Inkey(), the syntax would be like this:<br><br>DO WHILE<br>&nbsp;&nbsp;lnExit = INKEY(.06,[HM])<br>&nbsp;&nbsp;DO WHILE lnExit &lt;&gt; 0 && Keyboard buffer has no more keys<br>&nbsp;&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br>&nbsp;&nbsp;&nbsp;&nbsp;lnExit = INKEY(,[HM])<br>&nbsp;&nbsp;ENDDO<br>ENDDO<br><br>This way, if the user were to sit there tapping on the keyboard while it's processing, then click the mouse to cancel, your loop doesnt just process one key per loop iteration. Otherwise, your program must loop many more times than necessary to get to the mouse click in the keyboard buffer.<br><br>Try setting ON KEY LABEL LEFTMOUSE lnExit=151, just make sure lnExit is a public variable, or at least is in scope while the loop is processing.<br>You could use ON KEY LABEL ESC, but remember to SET ESCAPE OFF.<br><br>With either of these methods, where you are testing for Inkey() in your previous example, insert a DOEVENTS, but you may wanna execute it at certain intervals like in my forementioned example.<br><br>Good Luck<br><br>Jon
 
FWIW,<br><br>This loop only takes about 2.5 seconds.<br><br>FOR X = 1 TO 1000<br>&nbsp;&nbsp;if mod(x,100)=0<br>&nbsp;&nbsp;&nbsp;&nbsp;DOEVENTS<br>&nbsp;&nbsp;endif<br>ENDFOR<br><br>This loop takes about 105 seconds!<br><br>FOR X = 1 TO 1000<br>&nbsp;&nbsp;DOEVENTS<br>ENDFOR<br><br>This is on a PIII 550 w/ 256 megs of ram.<br><br>So use DOEVENTS wisely. :)<br><br>Jon<br>
 
DO EVENTS<br><br>In the loop I tested <b><i>DOEVENTS</b></i> it in do events is only called about every 25 cycles.&nbsp;&nbsp;There was no significant performance loss but your point is well taken. <p> <br><a href=mailto:blindpete@mail.com>blindpete@mail.com</a><br><a href= > </a><br>
 
oops forgot a sentence...<br><br>What I liked about the <b>DOEVENTS</b> command over other suggestions was the ease with which it could be implemented.&nbsp;&nbsp;Granted you must be judicious in it's application but programatically is was simple to do and test.&nbsp;&nbsp;It added a whopping 6 lines to my class's code... that is nice. <p> <br><a href=mailto:blindpete@mail.com>blindpete@mail.com</a><br><a href= > </a><br>
 
Jon<br><br>Your concerns about INKEY() are unjustified in this instance. You can tap away but the mouse click still triggers lnExit = .T. without the additional DO WHILE.. loop.<br><br>Neither ON KEY LABEL MOUSE or ON KEY LABEL ESC work.<br><br>Constant calling of INKEY() does, as indicated, periodically create an error message.<br><br>An unprofessional but successful fix is ON ERROR WAIT WINDOW [] NOWAIT for that loop. <br><br>Ignoring the use of DOEVENTS, the other issue is the increase in time taken by calling INKEY().<br><br>Any alternative to INKEY() would be welcome.<br><br>Current code as follows:<br><br>lnExit = 0<br>SCAN<br>&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;ENDIF <br>&nbsp;&nbsp;&nbsp;DO WHILE<br> lnExit = INKEY(.06,[HM])<br> IF lnExit = 151<br> &nbsp;&nbsp;&nbsp;lnAnswer = MESSAGEBOX([Do you want to cancel? etc<br> &nbsp;&nbsp;&nbsp;IF lnAnswer = 6 <br> EXIT<br> &nbsp;&nbsp;&nbsp;ENDIF<br> ENDIF<br><br> FOR<br> <br> ENDFOR <br><br> SCAN<br><br> ENDSCAN<br>ENDSCAN<br><br>Chris
 
Chris,<br><br><i>You can tap away but the mouse click still triggers lnExit = .T. without the additional DO WHILE.. loop.</i><br><br>&nbsp;&nbsp;True, but let me try and explain better. Using this code:<br>SCAN<br>&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;ENDIF <br>&nbsp;&nbsp;&nbsp;DO WHILE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnExit = INKEY(.06,[HM])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF <br>&nbsp;&nbsp;&nbsp;ENDDO<br>ENDSCAN<br><br>After the program executes SCAN, any input from the user is placed in the Keyboard buffer. If at this point the user presses the a key three times and then clicks the mouse. The first time inkey is executed, it returns 97 (asc for a), the second time 97, the third time 97, the fourth time 151 for the mouse click. It took 4 loop iterations to catch the mouse click. That's why I said to process the entire keyboard buffer. <br>Now dont get me wrong, to the naked eye you dont see this because the processor is so fast. But, If you want to accurately process the keypress as it happens, you need to process the entire keyboard buffer.<br><br>I got this from another developer:<br><br>IF MDOWN() OR CHRSAW()<br>&nbsp;&nbsp;DOEVENTS<br>ENDIF<br><br>This is better, IMO, than a DOEVENTS at certain intervals because it only fires when the user presses a key or clicks the mouse.
 
Jon<br><br>Apologies - I misunderstood you in that I took it that if the mouse click was not in the front of the buffer queue, it would not be detected. Thanks for taking the time to explain.<br><br>Unfortunately adding your second DO WHILE... loop effectively disables INKEY().<br><br>Picking up on your comments about DOEVENTS, the following code works and almost restores the original search times.<br><br>INKEY() is called every lnCycle at which point DOEVENTS is also fired<br><br>lnExit = 0<br>lnCount = 0<br>lnCycle = 75<br><br>SCAN<br>&nbsp;&nbsp;&nbsp;IF lnExit = 151<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EXIT<br>&nbsp;&nbsp;&nbsp;ENDIF <br>&nbsp;&nbsp;&nbsp;DO WHILE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnCount = lnCount + 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF lnCount &gt;= lnCycle<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* DOEVENTS<br> lnExit = INKEY(.06,[HM]) IF lnExit = 151<br> &nbsp;&nbsp;&nbsp;&nbsp;lnAnswer = MESSAGEBOX([Do you want to etc...<br> &nbsp;&nbsp;&nbsp;&nbsp;IF lnAnswer = 6<br> EXIT<br> &nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br> lnExit = 0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lnCount = 0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br><br><br>&nbsp;&nbsp;&nbsp;ENDDO <br>ENDSCAN <br><br>Would appreciate any comments.<br><br>Chris
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top