Im making an app using delphi and here is my code snippit
pnlMenu1.Visible := True;
Sleep(5000);
pnlMenu1.Visible := False;
pnlMenu2.Visible := True;
I need the user to still be able to do stuff while the Sleep() is running, but Sleep() stops the main thread which stops the program. Is there anything that i can replace the Sleep() with that will work? Like:
pnlMenu1.Visible := True;
//Wait 5 seconds but dont holt main thread
pnlMenu1.Visible := False;
pnlMenu2.Visible := True;
Use a TTimer. Enable the time after having made pnlMenu visible and from the OnTimer event, put the rest of your processing.
You can use a "waiting loop" wich calls ProcessMessages, but this has plenty of difficulties (Re-entering events) so be careful testing your application when user clicks everywhere.
I have found a solution. For anyone with same problem :
Add this somewhere in the code:
procedure Delay(dwMilliseconds: Longint);
var
iStart, iStop: DWORD;
begin
iStart := GetTickCount;
repeat
iStop := GetTickCount;
Application.ProcessMessages;
Sleep(1);
until (iStop - iStart) >= dwMilliseconds;
end;
Then replace 'Sleep(time)' With 'Delay(time)'
So:
Sleep(5000);
is replaced with
Delay(5000);
Related
I have a Delphi project that consists of two forms namely MainForm and DialogForm. When I click on Button1, the DialogForm should appear and stay on top until a process complete (the process takes a few seconds to complete).
The DialogForm includes a Timage component. When I click on the Button1 to show the DialogForm, the Gif image appears but without animation. This happens only when the process starts (without the process the animation works). What is the reason for this and how to keep the animation until closing the DialogForm?
procedure TMainForm.Button1Click(Sender: TObject);
var
gif: TGIFImage;
begin
Enabled:=false;
try
DialogForm.Show;
DialogForm.Refresh;
// The process is:
...
ipcAES1.Encrypt;//where ipcAES is part of the IPWorks Encrypt library
RichEdit1.Text:=ipcAES1.OutputMessage;
finally
Enabled:= true;
DialogForm.Close;
end;
end;
//---------------------------------------
procedure TDialogForm.FormShow(Sender: TObject);
var
gif: TGIFImage;
begin
gif := TGIFImage.Create;
gif.LoadFromFile('D:\preview.gif');
gif.Animate := True;
image1.Parent := Self;
image1.Left := 0;
image1.Top := 0;
image1.width := 800;
image1.height := 800;
image1.Picture.Assign(gif);
gif.Animate := True;
gif.Free;
end;
As said by many in this thread, because the processing is done in the main thread, the UI is not updated during this process.
To make sure the UI is updated while the process is running, let a separate thread do the processing:
procedure TForm1.Button1Click(Sender: TObject);
var
aProcessingThread: TThread;
begin
// First read all data needed by the process from UI controls (or other non-threadsafe parts)
<data> := ...;
// Then create a new (anonymous) thread with the code you need to run your process
aProcessingThread := TThread.CreateAnonymousThread(
procedure
begin
// create the objects you need to do the processing
ipcAES1 := Txxx.Create;
try
// Set the data
ipcAES1.<data> := <data>;
// Execute the proces:
// ...
ipcAES1.Encrypt;
finally
// When the process is done, use 'Synchronize' to interact with the UI
// again, so you can add the processed data to the RichtEdit and so on...
TThread.Synchronize(nil,
procedure
begin
// Now you can interact again with the UI
RichEdit1.Text := ipcAES1.OutputMessage;
Enabled:= true;
DialogForm.Close;
end);
ipcAES1.Free;
end;
end);
// The thread is now created, but not started/running, so you can now show
// the dialog and then start the thread, at which point the ButtonClick event
// exists, but the progress dialog is shown and the thread is running.
Enabled := False;
DialogForm.Show;
aProcessingThread.Start;
end;
Of course this only a basic example of how to use an (anonymous) thread to do some processing in the background.
Please note you need to handle Exceptions inside the thread (try/except).
A small tip regarding the TGifImage loading: you can just call Picture.LoadfromFile to load the gif as long as you include Vcl.Imaging.GIFImg in the uses clause.
procedure TForm1.FormShow(Sender: TObject);
begin
image1.Picture.LoadFromFile('D:\preview.gif');
image1.Parent := Self;
image1.Left := 0;
image1.Top := 0;
image1.width := Image1.Picture.Width;
image1.height := Image1.Picture.Height;
(image1.Picture.Graphic as TGIFImage).Animate := True;
end;
I'm starting using the OTL for a simple project but I'm currently stuck at stopping and restarting a simple parallel loop.
Use case
The user is able to start a query to resolve hostnames of a list of IP address by pressing a button. At any moment, he can press a refresh button that will stop the current loop (CancelParallelResolveHostnames) and restart hostname resolution (StartParallelResolveHostnames).
But in all my implementations, the executable is dead locked at waiting for the FParallelResolveHostnamesWait or simply when I try to set FParallelResolveHostnames to nil.
I also noticed that the OnStop callback is not called when the loop is canceled. Is it the normal behaviour ?
References
IOmniParallelSimpleLoop interface
Foreach loop cancelation
IOmniWaitableValue and TOmniWaitableValue
Sample code
var
FAddressToProcess: TStringList;
FParallelResolveHostnames: IOmniParallelSimpleLoop;
FParallelResolveHostnamesCancelToken: IOmniCancellationToken;
FParallelResolveHostnamesWait: IOmniWaitableValue;
procedure TGuiConnections.StartParallelResolveHostnames;
var
TaskConfig: IOmniTaskConfig;
begin
FParallelResolveHostnamesWait := CreateWaitableValue;
FParallelResolveHostnamesCancelToken := CreateOmniCancellationToken;
TaskConfig := Parallel.TaskConfig;
TaskConfig.SetPriority(TOTLThreadPriority.tpLowest);
FParallelResolveHostnames := Parallel //
.For(0, FAddressToProcess.Count - 1) //
.TaskConfig(TaskConfig) //
.CancelWith(FParallelResolveHostnamesCancelToken) //
.NoWait //
.OnStop(
procedure
begin
FParallelResolveHostnamesWait.Signal;
end); //
FParallelResolveHostnames.NoWait.Execute(
procedure(i: Integer)
begin
ResolveHostname(i, FAddressToProcess[i]);
end);
end;
procedure TGuiConnections.CancelParallelResolveHostnames;
begin
if Assigned(FParallelResolveHostnamesCancelToken) then
begin
FParallelResolveHostnamesCancelToken.Signal;
end;
if Assigned(FParallelResolveHostnamesWait) then
begin
FParallelResolveHostnamesWait.WaitFor();
FParallelResolveHostnamesWait := nil;
FParallelResolveHostnames := nil;
FParallelResolveHostnamesCancelToken := nil;
end;
end;
Sample projects:
Project 7z archive using IOmniWaitableValue
Project 7z archive using FParallelResolveHostnames := nil
I am finding difficulty in finding a solution for this. I am designing a simulation application and need to display graphs at certain intervals. I have a TTrackBar. I want to increment its values with a delay of 500ms between each step increase.
I wrote this method which is called from FormShow(Sender: TObject) procedure
PROCEDURE playTrackBar (t: Real);
VAR
v : REAL;
BEGIN
v := t;
while <CONDITION> do
BEGIN
v := v + 1;
if (v >= Form4.TrackBar1.Max) then
BEGIN
v := 0;
END;
Form4.TrackBar1.Value := v;
sleep(500);
END;
END;
I was looking for an event like trackbar.isClicked() which returns a boolean value so I could stop the while loop, but couldn't find any such function. When I used the value true at <CONDITION>, the application crashed (possibly because of the infinite loop).
Need some help in finding what the <CONDITION> should be. Would accept if any other possible solutions to achieve auto-incrementing/auto-playing the track bar is provided.
A program must not allocate all CPU resources. When you update a GUI component or wants input from the user, there must be time for the system to reflect those events. A sleep call is not the correct way to do this.
Use a TTimer event to animate the trackbar.
Put the TTimer on your form and set Enabled to false. Add an OnTimer event:
procedure TMyForm.OnTimer1(Sender: TObject);
var
trackPos : Integer;
begin
trackPos := TrackBar1.Value;
if trackPos >= TrackBar1.Max then begin // Time to stop animation
Timer1.Enabled := False; // Or move this line to another event
TrackBar1.Value := 0; // Reset the value to zero
Exit;
end;
TrackBar1.Value := trackPos + 1;
end;
In your form Show event, set the update interval and start the timer:
procedure TMyForm.FormShow(Sender: TObject);
begin
TrackBar1.Max := 200;
TrackBar1.Value := 0;
Timer1.Interval := 500;
Timer1.Enabled := True; // Start the animation
end;
The animation stops when the trackbar value reaches the maximum value.
If you want to stop the animation by an event (user clicks on something),
just move the timer enabled setting into this event.
This question already has answers here:
How can I wait until an external process has completed?
(4 answers)
Closed 6 years ago.
If I call ShellExecute from my delphi application,
how can I determine if that program that I called is done, so that I may return to my app and do some other stuff after that other program is done.
For Example, open notepad , after done and close it ,
show message in my application "Done!"
You need to use ShellExecuteEx with SEE_MASK_NOCLOSEPROCESS. Check this out and see my comments inline:
var
sei: TShellExecuteInfo;
exitCode: Cardinal;
begin
ZeroMemory(#sei, SizeOf(sei));
with sei do
begin
cbSize := SizeOf(sei);
fMask := SEE_MASK_NOCLOSEPROCESS; // Tell ShellExecuteEx to keep the process handle open
Wnd := WindowHandleIfNeeded; // Can be omitted
lpVerb := 'open';
lpFile := PChar(PathOfExeToRun);
lpParameters := PChar(ParametersToUse);
lpDirectory := PChar(WorkingDirectoryToUse); // Can be omitted
nShow := SW_NORMAL; // Can be omitted
end;
if ShellExecuteEx(#sei) then
begin
// I have encapsulated the different ways in begin/end and commented.
// *** EITHER: Wait for the child process to close, without processing messages (if you do it in a background thread)
begin
WaitForSingleObject(sei.hProcess, INFINITE);
end;
// *** OR: Wait for the child process to close, while processing messages (if you do it in the UI thread)
begin
while MsgWaitForMultipleObjects(1, sei.hProcess, FALSE, INFINITE, QS_ALLINPUT) = (WAIT_OBJECT_0 + 1) do begin
Application.ProcessMessages
end;
end;
// *** OR: Do something else, and in the middle you can check whether the child is still running using this:
begin
GetExitCodeProcess(sei.hProcess, exitCode);
if exitCode == STILL_ACTIVE then begin
// It's still running!
end else begin
// It has finished!
end;
end;
// At the end, close the handle
CloseHandle(sei.hProcess);
end;
end;
I'm programing one network application in delphi 6 using TTCPServer. At OnAccept event of this component I call showmodal for another form. This cause to hang the main form. I think that it is because of threads but I do not know how to showmodal the form at this event. I really have to call that.
I appreciate any answer. thanks.
var
s: array[0..10000] of byte;
i, j: integer;
Str : String;
Request, UN, Pass: WideString;
StartItemNode : IXMLNode;
st: TStringStream;
begin
CoInitialize(nil);
i := ClientSocket.ReceiveBuf(s, 10000, 0);
Str := '';
for j := 0 to i - 1 do
Str := Str + AnsiChar(s[j]);
XMLDoc.XML.Text := Str;
XMLDoc.Active := true;
StartItemNode := XMLDoc.ChildNodes.FindNode('r');
Request := StartItemNode.ChildNodes['request'].Text;
if(Request = 'Order')then
begin
Memo1.Lines.Text := Str;
ClientSocket.Sendln('<?xml version="1.0" encoding="utf-8"?><r><answer result="OK"></answer></r>');
**Form2.ShowModal;**
end;
Finally find the answer. with Indy component we can use TidSync or TidNotify. Create it and the call the Synchronize method of then with parameter of a method. this method with run at main thread(UI) and it is thread safe.
I can not find any solution for TTCPServer but we can use Timer. Set its Interval to for example 100 and do your showmodal at tick of that. but do not forgot about more than one running at same time. for example at OnTimer you have to disable recieving new connections or save them in array to process next OnTimer event.