QC20N
asked on
Timer and a ProgressBar
I have a timer on my form. The timerinterval is 5 min. I would like to have a progressbar that show me when it is time to run my procedure. You know, when the time reached 5 min.
While Hypo's approach will work, I would probably have a slightly different approach. I'd set up a second timer that drove the ProgressBar (again using a 1 second timer interval) but I'd leave the first one with its 5 minute (300 secconds or 300,000 "ticks") setting. When the 5 minute timer goes off, the first step of the event handler would be to disable both timers and then set the ProgressBar's progress to 300 (i.e. force it to completion ;-).
This avoids a slight discrepancy in the timer that would develop over the 300 events from the 1 second timer. It also lets you use the second timer and the progress bar for other activities as needed (when the 5 minute timer isn't running ;-).
This avoids a slight discrepancy in the timer that would develop over the 300 events from the 1 second timer. It also lets you use the second timer and the progress bar for other activities as needed (when the 5 minute timer isn't running ;-).
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I forgot to tell you, that I need a checkbox to start the timer with. And if I uncheck the checkbox the timer should not stop, but keep on goind to the end of my procedure and then stop.
How do I accomplish that?
How do I accomplish that?
hmmm, it fires 2 times in the end
procedure TMyThread.Execute;
var
Start: TDateTime;
Interval: Int64;
Temp: integer;
begin
Start := Now;
Interval := 300; // 5 * 60
Progress(0);
repeat
Sleep(500);
Temp := Integer(SecondsBetween(Now , Start) div 3); // Seconds / 300 * 100 = procent
Progress(Temp);
if Temp >= 300 then
begin
Start := Now;
Progress(-1);
end;
until Terminated;
end;
procedure TForm1.UpdateProgressBar(a Progress: Integer);
begin
ProgressBar1.Position := aProgress;
ProgressBar1.Update; // Make sure to repaint the progressbar
if aProgress = -1 then
PostMessage(Handle, WM_USER, 0, 0);
end;
procedure TMyThread.Execute;
var
Start: TDateTime;
Interval: Int64;
Temp: integer;
begin
Start := Now;
Interval := 300; // 5 * 60
Progress(0);
repeat
Sleep(500);
Temp := Integer(SecondsBetween(Now
Progress(Temp);
if Temp >= 300 then
begin
Start := Now;
Progress(-1);
end;
until Terminated;
end;
procedure TForm1.UpdateProgressBar(a
begin
ProgressBar1.Position := aProgress;
ProgressBar1.Update; // Make sure to repaint the progressbar
if aProgress = -1 then
PostMessage(Handle, WM_USER, 0, 0);
end;
type
TForm1 = class(TForm)
Checkbox1: TCheckbox;
procedure Checkbox1Click(Sender: TObject);
private
fStopWhenFinished: boolean;
end;
procedure TForm1.Checkbox1Click(Send er: TObject);
begin
if TCheckBox(Sender).Checked then
begin
fStopWhenFinished := False;
if not Asisgned(fMyThread) then
fMyThread := TMyThread.Create(UpdatePro gressBar);
end else
fStopWhenFinished := True;
end;
procedure TForm1.UpdateProgressBar(a Progress: Integer);
begin
ProgressBar1.Position := aProgress;
ProgressBar1.Update; // Make sure to repaint the progressbar
if aProgress = -1 then
begin
PostMessage(Handle, WM_USER, 0, 0);
if fStopWhenFinished then
begin
fMyThread.Terminate;
fMyThread := nil;
end;
end;
end;
TForm1 = class(TForm)
Checkbox1: TCheckbox;
procedure Checkbox1Click(Sender: TObject);
private
fStopWhenFinished: boolean;
end;
procedure TForm1.Checkbox1Click(Send
begin
if TCheckBox(Sender).Checked then
begin
fStopWhenFinished := False;
if not Asisgned(fMyThread) then
fMyThread := TMyThread.Create(UpdatePro
end else
fStopWhenFinished := True;
end;
procedure TForm1.UpdateProgressBar(a
begin
ProgressBar1.Position := aProgress;
ProgressBar1.Update; // Make sure to repaint the progressbar
if aProgress = -1 then
begin
PostMessage(Handle, WM_USER, 0, 0);
if fStopWhenFinished then
begin
fMyThread.Terminate;
fMyThread := nil;
end;
end;
end;
Re: Checkbox
When the user checks the box, in that event handler, start the timer if the checkbox is checked and ignore it if the checkbox is unchecked. THe question then arises, though, if I check the box, wait 20 seconds before unchecking the box, and then wait another 40 seconds before checking the box again, should the timer restart when I check it again?
Re: Form Freezing
Guys, "sleep" is not a Timer. A TTimer is a Timer. If you use a TTimer component, you can start the timer and then go on with whatever you are doing with the form. THe TTimer's event will interrupt things when it goes off and that will let you do whatever needs to be done based on the Timer's alarm going off, so to speak.
"Sleep" is a form of a "Let's pause for an interval, doing nothing, and then keep going." For something like a 5-minute interval, a "Sleep" is a seriously poor solution. You need to investigate the TTimer component.
When the user checks the box, in that event handler, start the timer if the checkbox is checked and ignore it if the checkbox is unchecked. THe question then arises, though, if I check the box, wait 20 seconds before unchecking the box, and then wait another 40 seconds before checking the box again, should the timer restart when I check it again?
Re: Form Freezing
Guys, "sleep" is not a Timer. A TTimer is a Timer. If you use a TTimer component, you can start the timer and then go on with whatever you are doing with the form. THe TTimer's event will interrupt things when it goes off and that will let you do whatever needs to be done based on the Timer's alarm going off, so to speak.
"Sleep" is a form of a "Let's pause for an interval, doing nothing, and then keep going." For something like a 5-minute interval, a "Sleep" is a seriously poor solution. You need to investigate the TTimer component.
First make sure that your Timer is disabled by defualt.
Then add a checkbox to your form, and set the OnClickEven of the checkbox to enable the timer if the box is checked.
Finally, in the OnTimer event, when the progress bar is full (when your 5 minutes have passed) you simply disable the timer if the checkbox is not set.
see sample below...
/Hypo
Then add a checkbox to your form, and set the OnClickEven of the checkbox to enable the timer if the box is checked.
Finally, in the OnTimer event, when the progress bar is full (when your 5 minutes have passed) you simply disable the timer if the checkbox is not set.
see sample below...
/Hypo
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
ProgressBar1: TProgressBar;
Memo1: TMemo;
CheckBox1: TCheckBox;
procedure Timer1Timer(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Timer1Timer(Sender: TObject);
begin
// Increase the ProgressBar...
ProgressBar1.StepBy(1);
// Check the progressBar...
if ProgressBar1.Position >= ProgressBar1.Max then begin
// Do your stuff here...
Memo1.Lines.Add('5 minutes passed. Do your stuff');
// Reset the ProgressBar for next tick
ProgressBar1.Position := ProgressBar1.Min;
// The timer should not continue if Checkbox is not checked...
Timer1.Enabled := CheckBox1.Checked;
end;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
Timer1.Enabled := True;
end;
end.
--- DFM ---
object Form1: TForm1
Left = 192
Top = 114
Width = 276
Height = 252
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object ProgressBar1: TProgressBar
Left = 8
Top = 40
Width = 150
Height = 17
Min = 0
Max = 5
TabOrder = 0
end
object Memo1: TMemo
Left = 8
Top = 64
Width = 185
Height = 89
Lines.Strings = (
'Memo1')
TabOrder = 1
end
object CheckBox1: TCheckBox
Left = 8
Top = 8
Width = 97
Height = 17
Caption = 'Enable timer'
TabOrder = 2
OnClick = CheckBox1Click
end
object Timer1: TTimer
Enabled = False
OnTimer = Timer1Timer
Left = 8
Top = 8
end
end
Hypo,
I actually thought that was the process being discuessed in the first place. ;-)
In your case, you have a single TTimer set to handle the 5 minute interval; however, that won't handle the progress bar. If, as I suggested, a second TTimer (Timer2) is dropped on the form and its interval set to 1000 (i.e. 1 second), then Timer2 can be used to control the progress indicated by the ProgerssBar. When Timer1 goes off, the event handler could disable Timer2 and force the max setting for the ProgressBar. If Timer2 causes teh Progress Bar to reach the max setting before Timer1 goes off, then Timer2 can disable itself (because Timer1 should be firing momentarily ;-).
I actually thought that was the process being discuessed in the first place. ;-)
In your case, you have a single TTimer set to handle the 5 minute interval; however, that won't handle the progress bar. If, as I suggested, a second TTimer (Timer2) is dropped on the form and its interval set to 1000 (i.e. 1 second), then Timer2 can be used to control the progress indicated by the ProgerssBar. When Timer1 goes off, the event handler could disable Timer2 and force the max setting for the ProgressBar. If Timer2 causes teh Progress Bar to reach the max setting before Timer1 goes off, then Timer2 can disable itself (because Timer1 should be firing momentarily ;-).
Oh, I just replied to QC20N's remark about the check box, but I'm at work so it took some time before I could post it, and so I didn't see your discussion... :)
At first I was thinking about using two timers as well, but I didn't like that the direct correlation between the timer and the progressbar got broken with two timers.
Then my only aproach to this problem is to make the code as easy to understand as possible (for the authors sake), and I think one timer is somewhat easier to understand than two timers (that could be debated though), but it's definatley easier than adding threads (although it's an interesting solution).
/Hypo
At first I was thinking about using two timers as well, but I didn't like that the direct correlation between the timer and the progressbar got broken with two timers.
Then my only aproach to this problem is to make the code as easy to understand as possible (for the authors sake), and I think one timer is somewhat easier to understand than two timers (that could be debated though), but it's definatley easier than adding threads (although it's an interesting solution).
/Hypo
Hypo,
No worries. ;-)
I have done a very similar thing to this in the past and it is truly amazing how much the little bit of code that moves the progress bar and re-enables the timer adds to the over all execution time of the 5 minute wait. That is why I would (as I did in the past) put the second timer in the picture. The first timer handles the "real" task of the 5-minute wait while the second timer handles the "cosmetic" task of moving the progress bar.
In the situation I was in in the past, I reused both timers and the progress bar for a variety of purposes but, by having the second timer set to a 1 second interval, the proress bar handling was reduces to setting the max, resetting the progress to 0, and enabling the timer. Similarly, the primary timer's interval could be set for whatever was needed and then the Tag property set to an integer that would be tested in the event handler (Good old Case statements ;-) to process whatever was needed.
No worries. ;-)
I have done a very similar thing to this in the past and it is truly amazing how much the little bit of code that moves the progress bar and re-enables the timer adds to the over all execution time of the 5 minute wait. That is why I would (as I did in the past) put the second timer in the picture. The first timer handles the "real" task of the 5-minute wait while the second timer handles the "cosmetic" task of moving the progress bar.
In the situation I was in in the past, I reused both timers and the progress bar for a variety of purposes but, by having the second timer set to a 1 second interval, the proress bar handling was reduces to setting the max, resetting the progress to 0, and enabling the timer. Similarly, the primary timer's interval could be set for whatever was needed and then the Tag property set to an integer that would be tested in the event handler (Good old Case statements ;-) to process whatever was needed.
>>although it's an interesting solution
lol, i thought my posts didn't get noticed
lol, i thought my posts didn't get noticed
ASKER
Geert:
Will it be possible to use a timer without using threads? As you know, I have another question here on EE about threads. I have looked this throug and thougt I might could use timers instead, cause my procedure only needs to run every 8th hour, so it will be 3 times on one day. And as you also know I can't figure out how threads works, sorry.
In fact in the end, my procedure is going to be run as a service and pleas don't tell me that it still needs to run as a thread. :)
Will it be possible to use a timer without using threads? As you know, I have another question here on EE about threads. I have looked this throug and thougt I might could use timers instead, cause my procedure only needs to run every 8th hour, so it will be 3 times on one day. And as you also know I can't figure out how threads works, sorry.
In fact in the end, my procedure is going to be run as a service and pleas don't tell me that it still needs to run as a thread. :)
QC20N,
If you need to have your process run every 8 hours, just use the Windows Task Scheduler and set it up to run at those times/that interval on the days you want it to run.
If you use a timer (or, heaven forbid, "Sleep") it is still going to consume resources because, technically, it is still running.
Using Timers doesn't require the use of threads, by the way . . . they sort of handle that aspect on their own. ;-)
If you need to have your process run every 8 hours, just use the Windows Task Scheduler and set it up to run at those times/that interval on the days you want it to run.
If you use a timer (or, heaven forbid, "Sleep") it is still going to consume resources because, technically, it is still running.
Using Timers doesn't require the use of threads, by the way . . . they sort of handle that aspect on their own. ;-)
>>going to be run as a service
ow, then let me show you this sample:
http://www.tolderlund.eu/delphi/service/service.htm
i used it for myself yesterday and i found it covered everything,
even logging in the windows event logger
>>pleas don't tell me
um yeah, in fact a service is a thread doing a loop processing messages until stop or finished
oh and if you want to run the service, you need to put /INSTALL in the run parameters
building a service is actually the step after you master creating/running threads
have you ever created a service application ?
if you create the thread in your other app and have it as standalone unit,
then creating the service app will only be a matter of starting and stopping that thread
i'm sorry to QC20N, but if you want to get somewhere with this,
you'll have to try and learn yourself threading in delphi
or find someone to do it for you
ow, then let me show you this sample:
http://www.tolderlund.eu/delphi/service/service.htm
i used it for myself yesterday and i found it covered everything,
even logging in the windows event logger
>>pleas don't tell me
um yeah, in fact a service is a thread doing a loop processing messages until stop or finished
oh and if you want to run the service, you need to put /INSTALL in the run parameters
building a service is actually the step after you master creating/running threads
have you ever created a service application ?
if you create the thread in your other app and have it as standalone unit,
then creating the service app will only be a matter of starting and stopping that thread
i'm sorry to QC20N, but if you want to get somewhere with this,
you'll have to try and learn yourself threading in delphi
or find someone to do it for you
problem with service applications is that you MUST handle all exceptions....
regarding the question:
First create a procedure to set off the timer (Timer1):
.....
private
procedure Blink;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Blink;
begin
Timer1.Enabled := True
end;
Now for the timer :
procedure TForm1.Timer1Timer(Sender: TObject);
begin
ProgressBar1.Visible:=True ; //progres bar should be invisible at first
Randomize;
if Random(1) = 0 then ProgressBar1.Position:=Pro gressBar1. Position+1 ;
if (ProgressBar1.Position = 20) then //use Max
begin
Timer1.Enabled := False;
ProgressBar1.Visible:=Fals e;
ShowMessage('Progress Bar Finished');
end;
end;
Now to trigger off the timer1 use timer2.Set it to run at right interval:
procedure TForm1.Timer2Timer(Sender: TObject);
begin
ProgressBar1.Position:=0;
Blink;
regarding the question:
First create a procedure to set off the timer (Timer1):
.....
private
procedure Blink;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Blink;
begin
Timer1.Enabled := True
end;
Now for the timer :
procedure TForm1.Timer1Timer(Sender:
begin
ProgressBar1.Visible:=True
Randomize;
if Random(1) = 0 then ProgressBar1.Position:=Pro
if (ProgressBar1.Position = 20) then //use Max
begin
Timer1.Enabled := False;
ProgressBar1.Visible:=Fals
ShowMessage('Progress Bar Finished');
end;
end;
Now to trigger off the timer1 use timer2.Set it to run at right interval:
procedure TForm1.Timer2Timer(Sender:
begin
ProgressBar1.Position:=0;
Blink;
hmmm ... service applications don't have forms (or progressbars ...)
so all the help we are providing needs to be reprogrammed anyway
it would be better to start in the service direction straight away
otherwise you will have to rethink nearly all your code again
so all the help we are providing needs to be reprogrammed anyway
it would be better to start in the service direction straight away
otherwise you will have to rethink nearly all your code again
ASKER
Reason for Partially is more my lack of knowledge, but I'm sure I will find out. :)
You can actually set the Max value of the Progressbar to the number of seconds you want on your actual timeout, and then use that as the trigger for your event.
So, set the timer timout to 1000 (1 sec) and then set Max value of your progress bar to 300 (5 mins = 300 sec), and use the Event from the snipet below for a small example.
/Hypo
Open in new window