tags:

views:

839

answers:

8

I've got an algorithm. I'd like to pause it at some point and then continue once the user presses a button. How do I do that? I've browsed the documentation, and searched the internet, but no luck yet.

Here's a relevant code snip:

      if A[i]>A[i+1]
        then
          begin
            Zameni(i,i+1);
            done:=true;

            sleep(pauza);

            br:=br+1;
          end;

Right now, I use sleep (pauza is just a constant, means pause in Serbian). Ideally, I'd like to replace that line with a procedure which would sleep for the interval, or wait for a button press based on a configuration setting.

EDIT1: Ah yes, if it wasn't obvious - it's a graphics application, not console, so slapping a "readln" won't work (sadly).

A: 

Just use ReadLn;

 ShowConsole("Enter to continue.");
 ReadLn;
Charlie Martin
+4  A: 

That's not how you do event-driven programming. You should do the processing up to the pause point and end your function at that point. In a button press event handler you then do the remainder of the processing - this will only happen when the user presses the button. You can call the same code from an OnTimer event to continue the processing after a given peruiod.

anon
Aye, but that's just the inner part of a for loop - I can post the whole function if that'd be more helpful.
VPeric
Sounds like you may need to rethink your design - big loops in event-driven (i.e. GUI) code are a bad idea unless they are in separate threads.
anon
Well, I didn't want to derail the main question, but I'm making a program to illustrate how various sorting algorithms work (by shuffling around variously colored graphs - see the Wikipedia page on QuickSort for my inspiration), so loops are needed. The idea is to let the user step through the sorting procedure, so they can understand it at their own pace.
VPeric
A loop is just a way of doing something multiple times. In an event-driven program, the user takes that role - each time they prtess the button, you could do one pass of what would have been in the loop - but there must be no actual loop.
anon
I agree, but why (and I could be just stupid, but also _how_ would I do it) should I bastardize a quicksort to make it "event driven"?
VPeric
You shouldn't - youu only need to do this because of your particular requirements. Normally one would run the sort (or any other potentially lengthy operation) in its own thread.
anon
As to how to make quicksort event-driven, that's quite hard because of its recursive nature. You will probably end up having to create and manage youer own stack to do this.
anon
Although, come to think of it, didn't Delphi itself use to come with a threading demo that showed various sorts graphically? Perhaps you could look that out.
anon
Aye, now that I've looked, it does - unfortunately, it doesn't "step" like I'm trying to do, so it's not too much of a help in that regard. Still, thanks for pointing it out, I'd never have looked myself, and it could be useful.
VPeric
A: 

You could launch a dialog which is what we do, a simple message box would be OK, i.e.


  procedure TForm1.Pause1Click(Sender: TObject);
  begin
    // A dialog box will halt thread execution
    Windows.MessageBox(0, 'Paused ...' + sLineBreak + 'Press Enter to Continue',
      '', MB_OK);
  end;

This probably only halts the current thread though, if you have more than one thread, you may want to try something more involved.

Brendan
It's one thread, and yeah, ShowMessage does what I need - the problem is that it also obscures the view and it looks ugly. If all else fails, I'll go for that (I think there's a way to determine where it'll appear), but it's not a particulary intuitive from a user standpoint. I'd prefer if he could just press a "normal" button.
VPeric
A: 

Try this

   if A[i]>A[i+1]
        then
          begin
            Zameni(i,i+1);
            done:=true;
            MessageDlg('Pause',mtConfirmation,mbYesNo,0);
            //sleep(pauza);
            br:=br+1;
          end;
RRUZ
A: 

I wouldn't recommend it as good application design, but if none of the other suggestions are suitable for you, you may prefer this.

Add a 'Paused: Boolean' field to your class/form, and a 'Continue' button.

When you start the operation, set Paused to False, and Continue.Enabled := False;

When your code reaches the section where you want to pause:

Paused := True;
Continue.Enabled := True;
while Paused do
begin
  sleep(100);
  Application.ProcessMessages;
end;

In your Continue buttons event handler:

procedure Form1.ContinueClick(Sender: TObject);
begin
  Paused := False;
  Continue.Enabled := False;
end;

As I said before, not pretty, but it should get the job done.

Mike Sutton
Since the Sleep() call won't let the user press the button you have just created an infinite loop. At least add an Application.ProcessMessages() call. Even then it will be a terrible solution.
mghie
I've updated the reply to include Application.ProcessMessages.
Mike Sutton
I'm afraid I'd have to agree with mghie, the problem with this solution is that you will see that your app uses up almost 100% of your cpu cycles during this loop.
Toby Allen
Obviously, a threaded implementation was the "right" way to do it, but this was vastly simpler and took care of the problem, so this is the solution I ended up using. As for CPU cycles, the sleep(100) bit assures the program will only wake 10 times a second, which isn't really a whole lot.
VPeric
+1  A: 

Store i in a static variable. Inside the loop add a check for a Pause flag (set by your pause button). If the flag is true, exit the function. When the pause button is clicked again call the function again. Check i's value, if it's already > 0 just enter the loop where you left off.

When the loop completes successfully, reset i to 0;

codeelegance
I think you have the best suggestion here.
Toby Allen
+2  A: 

Use a thread and the built-in suspend/resume functions for the TThread class. Jens Borrisholt wrote a nice little example article on about.com here doing this kind of thing.

If you introduce multiple ProcessMessage locations in the application, you can have problems with code execution from other events being left running when the form closes. For instance, if the form was closed while in the ProcessMessage "sleep" mode, there is no good way to unwind the stack.

MessageDialog may not work if you are using DirectX/OpenGL since you won't have the normal display open. Calling MessageDialog may switch the view from the DirectX view to normal desktop view (ugly but workable) OR it may show the message on the normal desktop view without changing the display mode from DirectX (making it impossible for the user to see the prompt). It's been a long time since I've worked with Dx, but I recall issues about failing to change view modes being a pain.

Edit: As Alexander points out, you shouldn't suspend the thread from anywhere but IN the thread (i.e., Thread A can suspend Thread A but not Thread B). If you want the GUI thread to suspend the sorting thread, send the sorting thread a message and handle the suspend inside the sorting thread. His link contains a good discussion on why this is so.

Marshall Fryman
Is this just not a bit too complicated for what he's trying to achieve?
Toby Allen
A: 

While I agree with thread approach, I think that using suspend/resume is a very bad idea. Here is why: http://blogs.msdn.com/oldnewthing/archive/2003/12/09/55988.aspx

Create a thread and use event to pause/resume thread. Simple. BTW, you can also use something like AsyncCalls to make thread creation easier (you don't need a TThread class).

Creating a separate thread is good for another reason: you could do heavy-computations in background, so they will not interfere with UI. Your app will have smooth reaction.

Alexander
There was something that went unstated in that article, so it's understandable that you missed it, although it's mentioned in the first and fifth comments there. It's perfectly safe for a thread to suspend itself. That's because it can't possibly be interrupting any other activity of the thread. That would be the case here: The thread would perform one step of the sort and then suspend itself.
Rob Kennedy
This link was actually a response to the example in the previous answer ;)
Alexander