3

I want to make a program wait a few seconds twice in one if statement. But my problem is that it skips the second Sleep command? I have searched for an answer but no one else seems to have this?

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  if Form1.shapeGreen.Brush.Color=clLime then
  begin
    Sleep(4000);
    Form1.shapeRed.Brush.Color:=clMaroon ;
    Form1.shapeYellow.Brush.Color:=clYellow;
    Form1.shapeGreen.Brush.Color:=clGreen;
    Sleep(3000);                            (it skips this (waits 0...))
    Form1.shapeRed.Brush.Color:=clRed ;
    Form1.shapeYellow.Brush.Color:=clOlive;
    Form1.shapeGreen.Brush.Color:=clGreen;
  end;
end;
NGLN
  • 43,011
  • 8
  • 105
  • 200
User049
  • 127
  • 1
  • 11
  • 3
    Since you are in a method of `TForm1`, it is unnecessary to specify all components with `Form1.`: you can use the reserved word `Self`, but since that is implicit, don't use it at all. The reason not to use `Form1` is that in future your form may be instantiated multiple times whereby each instance does not refer to the first instance. – NGLN Mar 16 '14 at 09:22
  • See also [Strange behaviour of function Sleep() used in repeat until in Delphi](http://stackoverflow.com/q/9656017/576719). – LU RD Mar 16 '14 at 09:44
  • 2
    You should be aiming to remove calls to Sleep rather than add them. ;-) – David Heffernan Mar 16 '14 at 15:42

2 Answers2

7

The second Sleep is not skipped; you wil see that you are unable to move your Form for 7 seconds.

With Sleep you make the application unresponsive in such way that it does not get time to update itself. Setting the color of a shape requests the parenting window to paint the shape again (with the new color settings), but these paint-requests are not handled, because you pause the application. Both color changes áre handled, but both paint-requests are packed into one, leaving only the latter to last.

An easy way is to update the Form yourself in between:

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  if ShapeGreen.Brush.Color = clLime then
  begin
    Sleep(4000);
    ShapeRed.Brush.Color := clMaroon;
    ShapeYellow.Brush.Color := clYellow;
    ShapeGreen.Brush.Color := clGreen;
    Update;
    Sleep(3000);                             
    ShapeRed.Brush.Color := clRed;
    ShapeYellow.Brush.Color := clOlive;
    ShapeGreen.Brush.Color := clGreen;
  end;
end;

But this still makes your application freeze twice. (Note that you cannot do anything in the application during the pauses). A better solution is to make use of a Timer to eliminate the use of Sleep all together:

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  if ShapeGreen.Brush.Color = clLime then
  begin
    Timer1.Interval := 4000;
    Timer1.Enabled := True;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if ShapeGreen.Brush.Color = clLime then
  begin
    ShapeRed.Brush.Color := clMaroon;
    ShapeYellow.Brush.Color := clYellow;
    ShapeGreen.Brush.Color := clGreen;
    Timer1.Interval := 3000;
  end
  else
  begin
    ShapeRed.Brush.Color := clRed;
    ShapeYellow.Brush.Color := clOlive;
    Timer1.Enabled := False;
  end;
end;

Next step is to make the logic in this code more rubust, for example with (a) variable(s) to decide which colors and intervals should be set.

NGLN
  • 43,011
  • 8
  • 105
  • 200
2

The program flow is event driven. Breaking the flow with Sleep() calls will turn your application non-responsive. In your case it will prevent updating colors in the first step. Hence it will look like it is skipping the last Sleep() call.

Use a Timer and a state variable to change your colors at given intervals instead.

Type
  TMyState = (msActivate,msFirstStep,msSecondStep);

  Tform1 = class(TForm)
  ...
  private
    fMyCounter: Integer;
    fMyState : TMyState;
  end;
// Timer1 is a TTimer with interval 1000 ms,
// initially disabled
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Inc(fMyCounter);
  case fMyState of
  msActivate: 
    begin
      fMyCounter := 0;
      fMyState := msFirstState;
    end; 
  msFirstState:
    if (fMyCounter = 4) then
    begin
      ShapeRed.Brush.Color := clMaroon;
      ShapeYellow.Brush.Color := clYellow;
      ShapeGreen.Brush.Color := clGreen;
      fMyState := msSecondState;
      fMyCounter := 0;
    end;
  msSecondState:
    if (fMyCounter = 3) then
    begin
      ShapeRed.Brush.Color := clRed;
      ShapeYellow.Brush.Color := clOlive;
      ShapeGreen.Brush.Color := clGreen;
      Timer1.Enabled := False;
    end;
  end;
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  if shapeGreen.Brush.Color=clLime then
  begin
    fMyState := msActivate;
    Timer1.Enabled := True;  // Trigger color change states
  end;
end;
LU RD
  • 34,438
  • 5
  • 88
  • 296
  • I know how to use a timer but i'm learning to do it without one. Thanks anyway :) – User049 Mar 17 '14 at 14:53
  • As you can see from the answers, Sleep is not the way to do it. It can be solved by threading, but in this case it will be more complex than a state machine using a timer. – LU RD Mar 17 '14 at 14:56