unit AniThread;
interface
uses
Classes, Windows, Controls, Graphics;
type
TAnimationThread = class(TThread)
private
{ private declarations }
FWnd: HWND;
FPaintRect: TRect;
FbkColor, FfgColor: TColor;
FInterval: integer;
protected
procedure Execute; override;
public
constructor Create(paintsurface : TWinControl; {Control to paint on }
paintrect : TRect; { area for animation bar }
bkColor, barcolor : TColor; { colors to use }
interval : integer); { wait in msecs between paints}
end;
implementation
constructor TAnimationThread.Create(paintsurface : TWinControl;
paintrect : TRect; bkColor, barcolor : TColor; interval : integer);
begin
inherited Create(True);
FWnd := paintsurface.Handle;
FPaintRect := paintrect;
FbkColor := bkColor;
FfgColor := barColor;
FInterval := interval;
FreeOnterminate := True;
Resume;
end; { TAnimationThread.Create }
procedure TAnimationThread.Execute;
var
image : TBitmap;
DC : HDC;
left, right : integer;
increment : integer;
imagerect : TRect;
state : (incRight, incLeft, decLeft, decRight);
begin
Image := TBitmap.Create;
try
with Image do
begin
Width := FPaintRect.Right - FPaintRect.Left;
Height := FPaintRect.Bottom - FPaintRect.Top;
imagerect := Rect(0, 0, Width, Height);
end; { with }
left := 0;
right := 0;
increment := imagerect.right div 50;
state := Low(State);
while not Terminated do
begin
with Image.Canvas do
begin
Brush.Color := FbkColor;
FillRect(imagerect);
case state of
incRight:
begin
Inc(right, increment);
if right > imagerect.right then
begin
right := imagerect.right;
Inc(state);
end; { if }
end; { case incRight }
incLeft:
begin
Inc(left, increment);
if left >= right then
begin
left := right;
Inc(state);
end; { if }
end; { case incLeft }
decLeft:
begin
Dec(left, increment);
if left <= 0 then
begin
left := 0;
Inc(state);
end; { if }
end; { case decLeft }
decRight:
begin
Dec(right, increment);
if right <= 0 then
begin
right := 0;
state := incRight;
end; { if }
end; { case decLeft }
end; { case }
Brush.Color := FfgColor;
FillRect(Rect(left, imagerect.top, right, imagerect.bottom));
end; { with }
DC := GetDC(FWnd);
if DC <> 0 then
try
BitBlt(DC,
FPaintRect.Left,
FPaintRect.Top,
imagerect.right,
imagerect.bottom,
Image.Canvas.handle,
0, 0,
SRCCOPY);
finally
ReleaseDC(FWnd, DC);
end;
Sleep(FInterval);
end; { while }
finally
Image.Free;
end;
InvalidateRect(FWnd, nil, True);
end; { TAnimationThread.Execute }
end.
//============================ HOW to USE IT ============================
{
Usage:
Place a TPanel on a form, size it as appropriate.Create an instance of the
TanimationThread call like this: procedure TForm1.Button1Click(Sender : TObject); }
var
ani : TAnimationThread;
r : TRect;
begin
r := Panel1.ClientRect;
InflateRect(r, - Panel1.BevelWidth, - Panel1.BevelWidth);
ani := TAnimationThread.Create(Panel1, r, Panel1.color, clBlue, 25);
Button1.Enabled := False;
Application.ProcessMessages;
Sleep(30000); // replace with query.Open or such
Button1.Enabled := True;
ani.Terminate;
ShowMessage('Done');
end.
Why my Thread run after Form creation?
-
08-07-2023 - |
Pergunta
I have a project that main Form
is created after my Thread
.
But this code don't work correctly:
type
TMyThread = class(TThread)
public
procedure Execute; override;
procedure doProc;
end; { type }
.
.
.
procedure TForm1.FormCreate(Sender: TObject);
var
thrd : TMyThread;
begin
thrd := TMyThread.Create(True);
thrd.Resume;
// Following code will cause the `Form` to show the time delay is about 5 seconds...
end;
.
.
.
procedure TMyThread.Execute;
begin
inherited;
doProc;
end;
procedure TMyThread.doProc;
var
AForm : TForm;
begin
AForm := TForm.Create(nil);
AForm.Caption := 'Thread Form';
AForm.Position := poScreenCenter;
AForm.FormStyle := fsStayOnTop;
AForm.Show;
end;
I don't want to use Synchronize
.
Is there a way to run my Thread
out of main Form
?
I want to show a Form
before the main Form
begin to create while the main Form
is shown.
Solução 6
Outras dicas
Mohammad, I think here is a classic example of total [multithreading] misunderstanding.
It is always good that you experiment with your code, but you must learn the basics. As David said, you are breaking the rules. The whole concept. You might be a thousand times frustrated because you code does not work, but VCL will not suddenly become thread-safe because of your frustrations.
My suggestion would be to RTFM. There is an excellent one.
A book by Martin Harvey, Multithreading - The Delphi Way. The reading is easy and fun for real geeks. You may not want to go beyond mutexes, critical sections and concurrency control, but at least you will learn some really important concepts about the amazing world of multithreading.
Please do not expect anyone here at SO to answer the wrong questions. The most of the responders are professional and experienced programmers and they wouldn't wanna waste their time to dive into the discussions which are initially wrong. People here are quite boolean ;)
In other words, try to learn some very basic things before you post your questions.
Your code breaks VCL threading rules. All VCL access must be on the main thread.
If you want to show GUI in a different thread (and very seldom is that a good idea) you need to use raw Win32 API calls. And run a message loop in the thread.
I cannot tell you how to solve your problem because I don't know what your problem is. But if all you want to do is show a form before the main form, just do that. No obvious need for a thread.
it may be way too obvious but why dont you just run your code before the Form is created? (the others already suggested something like that) if you create a new project and look at the source code of the project it will look like that:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
begin
(* Insert your code in here so it runs before the mainform is created *)
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Sorry, if i misunderstood your problem.
In answer to the question "why is my thread run after form creation?"
in TCustomForm.Create
, DoCreate
is called, which fires the OnCreate
event.
At that point the form itself is already created and will be shown right after the OnCreate
event returns.
In your case, you create the thread during this event.
Calling Resume
only tells the thread that it can execute, but is does not guarantee immediate execution (plus it will have issues, as David explained). It is likely the DoCreate
will finish before the Execute
method is called.
This also means that Synchronize
will not help, as that would be called from within the Execute context.
If you want to be certain that another form is first shown, you can do several things:
- Make the other form your main form and show your current main form when it closes (or a button is clicked, a timer runs out, etc)
- Put the code to display the other form in the
BeforeDestruction
method to have it execute before the constructor of the form. This will make it show first, but it will not prevent the main form from showing right after.
I hope this helps you.
QUESTIONS:
(1) Is there a way to run my Thread out of main Form?
(2) I want to show a Form before the main Form begin to create ...
(3) ...while the main Form is shown.
RESPONSES:
(1) Yes. Do the creation of thread out of the Main form (not inside the Oncreate event of Main form).
(2) OK. (same responde of (1)). Do the form creation (or Thread) before create the Main form (not in OnCreate event of Main Form).
(3) This is not possible with (1) and (2). Sorry.