Lav
Lav

Reputation: 31

Delphi stop procedure when button pressed

How can I stop a repeating loop in the middle of execution with pressing some button? The program only writes one letter into an edit box and then waits some given time, writes another letter and so on in a specified order, the pauses are made using sysutils.sleep.

Example:

procedure TForm1.Button4Click(Sender: TObject);
var
    j: integer;

begin
    edit1.Text:='a';
    edit1.Text:='a';
    sysutils.Sleep(l*20);
    edit1.Text:='b';
    edit1.text:='3';
    sysutils.sleep(l*20);
    j:= 1 ;

    repeat
        edit1.Text:='1';
        sysutils.sleep(l*2);
        edit1.Text:='0';
        edit1.text:='2';
        sysutils.sleep(l*2);
        edit1.Text:='3';
        edit1.text:='4';
        sysutils.sleep(l*2);
        edit1.Text:='5';
        sysutils.sleep(l*2);
        j:= j + 1;
    until j = 5;

end;

What I need: I press one button and it writes those letters, then whenever I press some other button, it stops the procedure and jumps to the other button procedures.

I know this is possible since a lot of apps can do a loop until you press something and stop a loop when you press a button, but I have no idea how to do this. Currently, the app freezes until it finishes the loop.

Upvotes: 3

Views: 4821

Answers (3)

Danny Rancher
Danny Rancher

Reputation: 2005

Use threads as in below application. Even thought the procedure is set up to run infinitely, the GUI remains unfrozen and you can stop the procedure.

enter image description here

http://www.delphibasics.info/home/delphibasicssnippets/threadswithparametersexample

Upvotes: -1

NGLN
NGLN

Reputation: 43659

Why this does not work

Every Windows application is message driven. The click on the button is actually a mouse click somewhere on the screen that Windows registers and translates into a message which is send to the application. The application has to process that message in order to determine its meaning before it eventually can call a procedure that will handle that mouse interaction. Your TForm1.Button4Click routine is such a procedure. (This procedure (or method) is called an event handler. The application converts the Windows message into an event: an application is based on an event driven architecture.)

When an application 1) is executing code, it cannot process messages. The code in TForm1.Button4Click, including all its loops, is executed in a single pass. And as long as it executes, it gives no change for the application to process another Windows message.

Why not to use Sleep?

From Sleep's documentation:

Suspends the execution of the current thread until the time-out interval elapses.

As you already found out yourself, Sleep makes an application 1) unresponsive and freezes. This is simply its characteristics.

How not to freeze the application?

By stopping execution of code: the currently running routine has to end. Only then can an application handle and respond to user interaction.

How to let things automatically run then?

By using something that still supports application's message and event handling, e.g. a Timer. A Timer is a component which, once Enabled, executes its OnTimer event handler at every passed Interval.

How to use a Timer in this situation?

Enable the Timer. At every interval, you can execute a piece of all code that is to be executed over longer time. In your case, that piece of code is setting an Edit's Text property. When multiple intervals are passed, and all code is executed, you disable the Timer.

Since the you have different waiting times and different values to be set, you must base the decision whether to update the Edit with what value on the time lapsed, or better: how many intervals have passed. Hold that count in a variable, and you're set.

1) The application's main thread.

Upvotes: 3

Tom Brunberg
Tom Brunberg

Reputation: 21033

As David said in his comment, you need to understand event driven programming. Very shortly said, when your program has started, it is simply waiting for events to occur. Events are e.g. button clicks, user typing something in an edit box, data is received from a comm port etc. Your job as a programmer is to write event handlers, that is, code that is triggered by these events (and hopefully does something useful).

You already have an event handler in your code, Button4Click. By your description you will also have another OnClick handler, maybe it is Button5Click. To achieve your task, you will also need a TTimer control, which has an OnTimer event. Event handlers should do as little as possible.

So, in your 'reaction game' you need three events:

  • Button4Click to start your 'game'
  • Timer1Timer event to change the character in the edit box
  • Button5Click to stop the 'game'.

In addition you need a storage for the characters to be displayed. Since you are showing single characters they can be stored in a string. If you would show strings in the edit box, you could use a TStringList as storage. To know which character to show at the next timer event you can use an integer variable as counter.

The Button4Click event handler would

  • initialize the character storage, let's call it s, and the counter variable, let's call it i.
  • show the first character in the edit box
  • set the timer interval
  • start the timer (at design time you would set Timer1.Enabled := false)

The Timer1Timer event handler would

  • stop the timer
  • increment the counter i,
  • show the character s[i]
  • set a new interval if needed
  • restart the timer

The Button5Click event handler would

  • stop the timer
  • whatever else you want it to do

Hopefully this gets you going. I did not to write any example code for you, so you can think about it yourself. But if you hit the wall, ask specific questions about your problem.

Upvotes: 5

Related Questions