I'm trying to make a progress bar that starts at 0%, and takes 5 seconds to get to 100%. The progress bar will begin to go up as soon as Button1 is clicked. Any advice? I looked on Google, but that gave me nothing good on this sort of thing.
Also, at 0%, there should be a label that says Waiting..., when the progress bar starts, it should go to Working..., and when it's done, it should say Done!.
You can use a timer with interval 50 and firstly set enabled to false.
procedure TForm1.Button1Click(Sender: TObject);
begin
timer1.Enabled := True;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
const
cnt: integer = 1;
begin
ProgressBar1.Position := cnt;
if cnt = 1 then Label1.Caption := 'Waiting...'
else if cnt = 100 then begin
Label1.Caption := 'Done!';
Timer1.Enabled := False;
end else
Label1.Caption := 'Working...';
Inc(cnt);
end;
Using GetTickCount() and initializing variables:
uses Windows;
var mseconds, starttime: integer;
procedore TForm1.FormCreate()
begin
starttime := GetTickCount();
mseconds := 0;
Timer1.Enabled := false;
Label1.Caption := '';
ProgressBar1.Position := 0;
Label1.Caption := 'Waiting...';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ProgressBar1.Min := 0;
ProgressBar.Max := 100;
ProgressBar1.Position := 0;
timer1.Enabled := True;
Label1.Caption := 'Working...';
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
mseconds := GetTickCount() - starttime;
if mseconds < 5000 then
ProgressBar1.Position := Trunc(mseconds / 50)
else begin
ProgressBar1.Position := 100;
Label1.Caption := 'Done!';
Timer1.Enabled := false;
end;
end;
Related
I have an app that allows my users to turn on and off a timer to track their time spent on a certain task. The timer runs a clock used to show the elapsed time to the user, much like a stopwatch.
The code below has worked as I thought it should for a few years now. However, when the app is run on Win 10, sometimes the "time" rate speeds up by 2 or 3 times during a session. If the user restarts the app, it may run at normal speed.
Win 10 Delphi 10.3
procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
if btnTimer.Caption = 'Start &Timer' then
begin
btnTimer.Down := True;
btnTimer.Caption := 'Stop &Timer';
pnlTimer.Color := clPurple;
btnResume.Enabled := True;
btnAssign.Enabled := False;
Timer1.Enabled := true;
UpdateTimer.Enabled := True;
ElapsedTime := ElapsedTime;
//btnPostRecord.Enabled := False;
btnCancel.Enabled := False;
btnDeleteTimeCard.Enabled := False;
end
else
begin
btnTimer.Down := False;
btnTimer.Caption := 'Start &Timer';
pnlTimer.ParentColor := True;
btnResume.Enabled := False;
btnAssign.Enabled := True;
pnlTimer.Color := clMoneyGreen;
end;
end;
procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
if btnTimer.Caption = 'Stop &Timer' then
begin
ElapsedTime := ElapsedTime + 0.0000115740;
cxClock1.time := ElapsedTime;
cxTimeEditTimer.Time := ElapsedTime;
end;
end;
This is a terrible way to keep track of elapsed time with a TTimer. TTimer is not a real-time timer, or even an accurate timer. It is based on the WM_TIMER window message, which is
a low-priority message. The GetMessage and PeekMessage functions post this message only when no other higher-priority messages are in the thread's message queue.
Don't calculate your ElapsedTime based on how often the TTimer fires its OnTimer event. Keep track of the current time when starting the TTimer, and then subtract that value from the next current time whenever the OnTimer event is eventually generated. That will give you a more real elapsed time.
Try something more like this:
uses
..., System.DateUtils;
private
StartTime: TDateTime;
ElapsedSecs: Int64;
procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
if btnTimer.Tag = 0 then
begin
btnTimer.Tag := 1;
...
ElapsedSecs := 0;
StartTime := Now;
Timer1.Enabled := true;
...
end
else
begin
btnTimer.Tag := 0;
...
ElapsedSecs := SecondsBetween(Now, StartTime);
Timer1.Enabled := false;
...
end;
end;
procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
if btnTimer.Tag = 1 then
begin
ElapsedSecs := SecondsBetween(Now, StartTime);
// use ElapsedSecs as needed ...
end;
end;
Or:
uses
..., Winapi.Windows;
private
StartTime: DWORD;
ElapsedSecs: Integer;
procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
if btnTimer.Tag = 0 then
begin
btnTimer.Tag := 1;
...
ElapsedSecs := 0;
StartTime := GetTickCount;
Timer1.Enabled := true;
...
end
else
begin
btnTimer.Tag := 0;
...
ElapsedSecs := (GetTickCount - StartTime) div 1000;
Timer1.Enabled := false;
...
end;
end;
procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
if btnTimer.Tag = 1 then
begin
ElapsedSecs := (GetTickCount - StartTime) div 1000;
// use ElapsedSecs as needed ...
end;
end;
Or:
uses
..., System.Diagnostics;
private
SW: TStopwatch;
ElapsedSecs: Integer;
procedure TfmTimeCard.btnTimerClick(Sender: TObject);
begin
if not SW.IsRunning then
begin
...
ElapsedSecs := 0;
SW := TStopWatch.Start;
Timer1.Enabled := true;
...
end
else
begin
...
SW.Stop;
ElapsedSecs := Trunc(SW.Elapsed.TotalSeconds);
Timer1.Enabled := false;
...
end;
end;
procedure TfmTimeCard.Timer1Timer(Sender: TObject);
begin
if SW.IsRunning then
begin
ElapsedSecs := Trunc(SW.Elapsed.TotalSeconds);
// use ElapsedSecs as needed ...
end;
end;
Say that I have five TRectangle objects, and a function is going to pass a parameter in to make one of them blink.
I know how to control one object like the following code:
procedure TForm1.TimerTimer(Sender: TObject);
begin
if rect1.Visible then
rect1.Visible := false
else
rect1.Visible := true;
end;
procedure TForm1.Blink_Square;
begin
Timer := TTimer.Create(nil);
Timer.OnTimer := TimerTimer;
rect1.Fill.Color := TAlphacolors.Red;
rect1.fill.Kind := TBrushKind.bkSolid;
rect1.Stroke.Thickness := 1;
rect1.Stroke.Color := Talphacolors.Darkgray;
Timer.Interval := 500;
Timer.Enabled := True;
end;
But I really wonder if there is a way that I can use the blink square repeatedly like having a procedure as procedure TForm1.Blink_Square(rec_number: integer); And we can call Blink_Square(5); to make rect5 blink.
Thanks in Advance
You can store your objects in an array or list, then use your procedure parameter to index into it.
var
Blinks: array[1..5] of record
Rectangle: TRectangle;
Timer: TTimer;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Blinks[1].Rectangle := Rect1;
Blinks[1].Timer := nil;
Blinks[2].Rectangle := Rect2;
Blinks[2].Timer := nil;
Blinks[3].Rectangle := Rect3;
Blinks[3].Timer := nil;
Blinks[4].Rectangle := Rect4;
Blinks[4].Timer := nil;
Blinks[5].Rectangle := Rect5;
Blinks[5].Timer := nil;
end;
procedure TForm1.TimerTimer(Sender: TObject);
var
Timer: TTimer;
begin
Timer := TTimer(Sender);
Blinks[Timer.Tag].Visible := not Blinks[Timer.Tag].Visible;
end;
procedure TForm1.Blink_Square(Number: Integer);
begin
Blinks[Number].Rectangle.Fill.Color := TAlphacolors.Red;
Blinks[Number].Rectangle.fill.Kind := TBrushKind.bkSolid;
Blinks[Number].Rectangle.Stroke.Thickness := 1;
Blinks[Number].Rectangle.Stroke.Color := Talphacolors.Darkgray;
if Blinks[Number].Timer = nil then
begin
Blinks[Number].Timer := TTimer.Create(Self);
Blinks[Number].Timer.OnTimer := TimerTimer;
Blinks[Number].Timer.Interval := 500;
Blinks[Number].Timer.Tag := Number;
Blinks[Number].Timer.Enabled := True;
end;
end;
Alternatively:
var
Rects: array[1..5] of TRectangle;
procedure TForm1.FormCreate(Sender: TObject);
begin
Rects[1] := Rect1;
Rects[2] := Rect2;
Rects[3] := Rect3;
Rects[4] := Rect4;
Rects[5] := Rect5;
end;
procedure TForm1.TimerTimer(Sender: TObject);
begin
TRectangle(Sender).Visible := not TRectangle(Sender).Visible;
end;
procedure TForm1.Blink_Square(Number: Integer);
var
Rec: TRectangle;
Timer: TTimer;
M: TNotifyEvent;
begin
Rec := Rects[Number];
Rec.Fill.Color := TAlphacolors.Red;
Rec.fill.Kind := TBrushKind.bkSolid;
Rec.Stroke.Thickness := 1;
Rec.Stroke.Color := Talphacolors.Darkgray;
if Rec.Tag = 0 then
begin
M := TimerTimer;
TMethod(M).Data := Rec;
Timer := TTimer.Create(Rec);
Timer.OnTimer := M;
Timer.Interval := 500;
Timer.Enabled := True;
Rec.Tag := NativeInt(Timer);
end;
end;
If I create multiple TPanel and TSplitter components at runtime into a TScrollBox, the order of the components is wrong. If I call drawInput() 3 times, the scrollbox contains 3 panels followed by 3 splitters instead of 1 panel followed by 1 splitter (repeated).
How can I force the correct order?
Here is a screenshot
function drawInput(owner: TWinControl): TWinControl;
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
panel := TPanel.Create(owner);
panel.Parent := owner;
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
edit := TEdit.Create(panel);
edit.Parent := panel;
edit.Align := alTop;
edit.AlignWithMargins := True;
edit.Text := 'foo';
edit.Margins.Left := 5;
edit.Margins.top := 5;
edit.Margins.Bottom := 5;
edit.Margins.Right := 5;
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
end;
EDIT:
Here is how I call this function:
procedure TForm2.Button1Click(Sender: TObject);
var
form: TForm;
sb: TScrollBox;
begin
form := TForm.Create(Application);
sb := TScrollBox.Create(form);
sb.Parent := form;
sb.Align := alClient;
sb.Color := clBlack;
drawInput(sb);
drawInput(sb);
drawInput(sb);
drawInput(sb);
form.Width := 300;
form.Height := 700;
form.ShowModal;
end;
Position your panel + splitter then set the alignment
You can position you panel below all other components by aligning it to the client
function drawInput(owner: TWinControl): TWinControl;
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
panel := TPanel.Create(owner);
panel.Parent := owner;
///
panel.Align := alclient;
///
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
//
splitter.top := panel.top+panel.height;
//
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
end;
Delphi's alignment logic can be hard at times. But the following works. Note the line splitter.Top := -1;
function drawInput(owner: TWinControl): TWinControl;
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
splitter.Top := -1;
panel := TPanel.Create(owner);
panel.Parent := owner;
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
edit := TEdit.Create(panel);
edit.Parent := panel;
edit.Align := alTop;
edit.AlignWithMargins := True;
edit.Text := 'foo';
edit.Margins.Left := 5;
edit.Margins.top := 5;
edit.Margins.Bottom := 5;
edit.Margins.Right := 5;
end;
Here's the code that works for me on XE5. I still have to solve my problem but at least I fixed yours :)
procedure drawInput(owner: TWinControl; var t: integer);
var
panel: TPanel;
edit: TEdit;
splitter: TSplitter;
begin
panel := TPanel.Create(owner);
panel.Parent := owner;
panel.Align := alTop;
panel.AlignWithMargins := True;
panel.BorderWidth := 0;
panel.Color := clRed;
panel.BorderStyle := bsNone;
panel.ParentBackground := False;
panel.Ctl3D := False;
panel.Top := t;
t := panel.Top + panel.Height + 1;
edit := TEdit.Create(panel);
edit.Parent := panel;
edit.Align := alTop;
edit.AlignWithMargins := True;
edit.Text := 'foo';
edit.Margins.Left := 5;
edit.Margins.top := 5;
edit.Margins.Bottom := 5;
edit.Margins.Right := 5;
splitter := TSplitter.Create(owner);
splitter.Parent := owner;
splitter.Align := alTop;
splitter.Beveled := True;
splitter.Height := 3;
splitter.Top := t;
t := splitter.Top + splitter.Height + 1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
form: TForm;
sb: TScrollBox;
t: integer;
begin
form := TForm.Create(Application);
sb := TScrollBox.Create(form);
sb.Parent := form;
sb.Align := alClient;
sb.Color := clBlack;
t := 0;
drawInput(sb, t);
drawInput(sb, t);
drawInput(sb, t);
drawInput(sb, t);
form.Width := 300;
form.Height := 700;
form.ShowModal;
end;
In one of my applications, I have a function that creates a TImage and follows it with a TSplitter with the parent and containing control being a TScrollbox (sbScroller). The function is either called by the end user (tied to a TButton OnClick event) when they select an image or when the program starts it loads a previously loaded set of images each divided by a TSplitter.
It works when run alone by itself (creating one TImage + TSplitter pairing) or when run in a continuous loop to create multiple pairings. The key element in getting it to work seems to the positioning of the TSplitter.Top property as the previous answer says:
procedure AddImage(AFilename: string);
var
Image: TImage;
begin
Image := TImage.Create(sbScroller);
with Image do
begin
Image.Parent := sbScroller;
Left := 0;
Top := 0;
Width := 150;
Height := 150;
Constraints.MinHeight := 128;
Align := alTop;
Anchors := [akLeft, akTop, akRight];
Proportional := True;
Stretch := True;
Visible := True;
end;
if sbScroller.ControlCount > 0 then
with TSplitter.Create(sbScroller) do
begin
Parent := sbScroller;
Top := Image.Top;
Align := alTop;
Color := clGray;
end;
end;
I am a delphi learner. I am having one Delphi Progect with "MainForm", "MinimizeTimer" and "RestoreTimer". I have defined the following codes.
Minimize Timer :
if MainForm.AlphaBlendValue >= 225 then
begin
MinimizeTimer.Enabled := true;
MainForm.AlphaBlendValue := MainForm.AlphaBlendValue - 5;
end;
Restore Timer :
if MainForm.AlphaBlendValue >= 0 then
begin
RestoreTimer.Enabled := true;
MainForm.AlphaBlendValue := MainForm.AlphaBlendValue + 5;
end;
My requirement is that the MainForm will be first fadeout using "MinimizeTimer" and then will be minimized when "_" Button on Caption Bar is pressed. And also be fadein using "RestoreTimer" and then will be restored after clicking on taskbar. So I defined again the following codes:
private
{ Private declarations }
procedure WMSysCommand(var Msg: TWMSysCommand) ; message WM_SYSCOMMAND;
..
..
..
..
..
procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand) ;
begin
if Msg.CmdType = SC_MINIMIZE then MinimizeTimer.Enabled := true;
DefaultHandler(Msg);
if Msg.CmdType = SC_RESTORE then RestoreTimer.Enabled := true;
DefaultHandler(Msg);
end;
But I am not getting the expected result. The MainForm is Minimized and Restored as in regular way. Please remember in my project I have one "FormCloseQuery" event also.
Please help me.
You are using the wrong logic for your requirements. Try this instead:
procedure TMainForm.MinimizeTimerTimer(Sender: TObject);
begin
if AlphaBlendValue > 0 then
begin
AlphaBlendValue := AlphaBlendValue - 5;
end
else
begin
MinimizeTimer.Enabled := False;
Perform(WM_SYSCOMMAND, SC_MINIMIZE, 0);
end;
end;
procedure TMainForm.RestoreTimerTimer(Sender: TObject);
begin
if AlphaBlendValue < 255 then
begin
AlphaBlendValue := AlphaBlendValue + 5;
end else begin
RestoreTimer.Enabled := False;
end;
end;
procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
case Msg.CmdType of
SC_MINIMIZE: begin
if AlphaBlendValue > 0 then
begin
MinimizeTimer.Enabled := True;
Exit;
end;
end;
SC_RESTORE: begin
if AlphaBlendValue < 255 then begin
RestoreTimer.Enabled := True;
end;
end;
end;
inherited;
end;
I want to create a warning dialog box which asks the users if the information typed during signup was correct, and asks him wether he want to continue or close that dialog and correct his information.
var
td: TTaskDialog;
tb: TTaskDialogBaseButtonItem;
begin
td := TTaskDialog.Create(nil);
try
td.Caption := 'Warning';
td.Text := 'Continue or Close?';
td.MainIcon := tdiWarning;
td.CommonButtons := [];
tb := td.Buttons.Add;
tb.Caption := 'Continue';
tb.ModalResult := 100;
tb := td.Buttons.Add;
tb.Caption := 'Close';
tb.ModalResult := 101;
td.Execute;
if td.ModalResult = 100 then
ShowMessage('Continue')
else if td.ModalResult = 101 then
ShowMessage('Close');
finally
td.Free;
end;
end;
Note: This will only work on Windows Vista or later.
if delphi then
if mrYes=MessageDlg('Continue?',mtwarning,[mbYes, mbNo],0) then
begin
//do somthing
end
else
exit; //go out
var
AMsgDialog: TForm;
abutton: TButton;
bbutton: TButton;
begin
AMsgDialog := CreateMessageDialog('This is a test message.', mtWarning,[]);
abutton := TButton.Create(AMsgDialog);
bbutton := TButton.Create(AMsgDialog);
with AMsgDialog do
try
Caption := 'Dialog Title' ;
Height := 140;
AMsgDialog.Width := 260 ;
with abutton do
begin
Parent := AMsgDialog;
Caption := 'Continue';
Top := 67;
Left := 60;
// OnClick :tnotyfievent ;
end;
with bbutton do
begin
Parent := AMsgDialog;
Caption := 'Close';
Top := 67;
Left := 140;
//OnClick :tnotyfievent ;
end;
ShowModal ;
finally
abutton.Free;
bbutton.Free;
Free;
end;
Based on this:
procedure HookResourceString(rs: PResStringRec; newStr: PChar);
var
oldprotect: DWORD;
begin
VirtualProtect(rs, SizeOf(rs^), PAGE_EXECUTE_READWRITE, #oldProtect);
rs^.Identifier := Integer(newStr);
VirtualProtect(rs, SizeOf(rs^), oldProtect, #oldProtect);
end;
const
SContinue = 'Continue';
SClose = 'Close';
procedure TForm1.Button1Click(Sender: TObject);
begin
HookResourceString(#SMsgDlgOK, SContinue);
HookResourceString(#SMsgDlgCancel, SClose);
if MessageDlg('My Message', mtConfirmation, [mbOK, mbCancel], 0) = mrOK then
begin
// OK...
end;
end;