how can i pause and resume in application while working with loops
i can put some sleep(xxx) at begininsg of my loop to pause ,but i want pause when ever i want and resume when i ever i need
any ideas ?
thanks in advance
ok here is alittle more explanation
for i:=0 to 100 do
begin
if button1.clicked then pause //stop the operation and wait for resume button
if button2.clicked then resume //resume the operations
end;
edit2 :
ok guys i will tell an example well take any checker for suppose proxy checker ,i have a bunch of proxies loaded in my tlistviwe i am checking them all ,by using lop for i:=0 to listview.items.count do ......
i want to pause my checking operation when ever i want and resume when ever i need
am i clear or still i have to explain some ? :S
regards
You need a boolean flag that will indicate whether it's safe to continue looping. If something happens that makes it so you need to pause, it should set the variable to false. This means that, unless you're working with multiple threads, whatever sets this flag will have to be checked inside the loop somewhere. Then at the top (or the bottom) of your loop, check to see if this variable is true and pause otherwise.
Here's the basic idea, in the context of your explanation:
procedure TForm1.DoLoop;
begin
FCanContinue := true;
for i:=0 to 100 do
begin
//do whatever
Application.ProcessMessages; //allow the UI to respond to button clicks
if not FCanContinue then Pause;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FCanContinue := false;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
FCanContinue := true;
Resume;
end;
This is a very simplistic implementation. What you really should do if you have a long-running task that you need to be able to pause and resume on command is put this in a separate thread and take a look at the TEvent class in SyncObjs.
OK, I don't understand what you are trying to do, but here is some pseudo code:
for i:=0 to 100 do
begin
if button1.clicked then
begin
while not button2.clicked do
sleep(50);
end;
end;
Check if a key is pressed?
Related
I am using a TEdit to allow the user to enter a number, e.g. 10.
I convert the TEdit.Text to an integer and a calculation procedure is called.
In this calc procedure, a check was built-in to make sure no numbers below 10 are processed.
Currently I use the OnChange event. Suppose the user wants to change '10' into e.g.'50'. But as soon as the '10' is deleted or the '5' (to be followed by the 0) is typed, I trigger my warning that the minimum number is 10. I.e. the program won't wait until I have fully typed the 5 and 0.
I tried OnEnter, OnClick, OnExit, but I seem not to overcome this problem. The only solution is to add a separate button that will trigger the calculation with the new number. It works, but can I do without the button?
Use a timer for a delayed check, e.g.:
procedure TForm1.Edit1Change(Sender: TObject);
begin
// reset the timer
Timer1.Enabled := false;
Timer1.Enabled := true;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := false;
// do your check here
end;
Setting the timer to 500 ms should be fine for most users.
And as David suggested in the comments to the question: Do not show an error dialog, use something less intrusive instead, e.g. an error message in a label near the edit or change the background color. And also: Do not prevent the focus to be moved away from that control and do not play a sound, that's also very annoying.
For our in house software we set the background of a control to yellow if there is an error and display the error message of the first such error in the status bar and also as a hint of the control. If you do that, you probably don't even need to have the delay.
Thanks, for your help. I tried the timer option, but could not get that to work. I now have this code, which works (almost - see below), but requires the used to always type a CR:
procedure Calculate;
begin
// this is my calculation procedure
ShowMessage('calculation running correctly');
end;
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
var
N : integer;
begin
if Key = #13 then
begin
N := StrtoInt(Edit1.Text);
if N<10 then ShowMessage('<10!') else
if N>100 then ShowMessage('>100!') else Calculate;
end;
end;
I used the ShowMessage() here only to see if the sample code worked. In the real program I have left that out, as you all suggested.
I also included the 'turn yellow on wrong entry' (thanks David). The only issue is that if the user runs this I get a beep from my computer. I can't see what went wrong. But what is causing the beep?
I need to embed a trivial MP3 player in a Delphi7 application.
I will simply scan a directory and play all the files in a random order.
I found two possible solutions: one using the Delphi MediaPlayer, and one using the PlaySound Windows API.
None are working.
The problem seems to be in the missing "stop" notification.
Using PlaySound like this:
playsound(pchar(mp3[r].name), 0, SND_ASYNC or SND_FILENAME);
I could not find a way to (politely) ask Windows to inform me when a song stopped playing.
Using the Delphi MediaPlayer, internet is full of suggestions copy/pasted one from the other, like here:
http://www.swissdelphicenter.ch/en/showcode.php?id=689
http://delphi.cjcsoft.net/viewthread.php?tid=44448
procedure TForm1.FormCreate(Sender: TObject);
begin
MediaPlayer1.Notify := True;
MediaPlayer1.OnNotify := NotifyProc;
end;
procedure TForm1.NotifyProc(Sender: TObject);
begin
with Sender as TMediaPlayer do
begin
case Mode of
mpStopped: {do something here};
end;
//must set to true to enable next-time notification
Notify := True;
end;
end;
{
NOTE that the Notify property resets back to False when a
notify event is triggered, so inorder for you to recieve
further notify events, you have to set it back to True as in the code.
for the MODES available, see the helpfile for MediaPlayer.Mode;
}
My problem is that I do get a NotifyValue == nvSuccessfull when a song is over but ALSO when I start a song, so I cannot rely on it.
Furthermore, I never, ever receive a change in state of the "mode" property, that should become mpStopped according to all the examples I found.
There is a similar question here
How can I repeat a song?
but it does not work because, as said, I receive the nvSuccessfull twice, without a way to distinguish between the start and the stop.
Last but not least, this app should work from XP to Win10, that's why I am developing with Delphi7 on WinXP.
Thank you and sorry for the length of this post, but I really tried many solutions before asking for help.
To detect when to load a new file for playing, you can use the OnNotify event and the EndPos and Position properties of the TMediaPlayer (hereafter called MP)
First setup the MP and select a TimeFormat, for example
MediaPlayer1.Wait := False;
MediaPlayer1.Notify := True;
MediaPlayer1.TimeFormat := tfFrames;
MediaPlayer1.OnNotify := NotifyProc;
When you load a file for playing, set the EndPos property
MediaPlayer1.FileName := OpenDialog1.Files[NextMedia];
MediaPlayer1.Open;
MediaPlayer1.EndPos := MediaPlayer1.Length;
MediaPlayer1.Play;
And the OnNotify() procedure
procedure TForm1.NotifyProc(Sender: TObject);
var
mp: TMediaPlayer;
begin
mp:= Sender as TMediaPlayer;
if not (mp.NotifyValue = TMPNotifyValues.nvSuccessful) then Exit;
if mp.Position >= mp.EndPos then
begin
// Select next file to play
NextMedia := (NextMedia + 1) mod OpenDialog1.Files.Count;
mp.FileName := OpenDialog1.Files[NextMedia];
mp.Open;
mp.EndPos := mp.Length;
mp.Position := 0;
mp.Play;
// Set Notify, important
mp.Notify := True;
end;
end;
Finally a comment to your attempt to use the MP.Mode = mpStopped mode to change to a new song. The mode is changed when the buttons are operated, iow mpStopped when the user presses the Stop button. Changing the song and starting to play it would likely not be what the user expected.
I am using OTL for the first time and I was trying to use the Async/Await abstraction.
Now, I created a small program just to see what will happen. It's just a button and it calls this procedure.
procedure TForm2.Button1Click(Sender: TObject);
var i : integer;
begin
Button1.enabled := false; //Only for second try
for i := 0 to 100 do
begin
Async(
procedure begin
sleep(5000);
end).
Await(
procedure begin
//First Try - Button1.Enabled := true;
//Second Try - showmessage('finished')
end
);
Button1.enabled := true; //Only for the second try.
end;
end;
First Try
For this it works fine the first time, disable the button, sleep for the asyncs and then enable it back.
But the second time I click the button, it is disabled but never gets enabled again.
Second Try
This time I wanted to show a message x100 times and it works the first time aswell, but when I call the procedure again I get the following error TOminCommunicationEndpoint.Send: Queue is full
Can someone who has used this library explain to me, why am I getting this error? And if it is related to the problem with the first try?
It seems you are hitting an internal limitation of OTL.
Each call to Async-Await starts a new thread and returns immediately. When the loop is done you end up with 100 threads, each waiting 5 seconds before executing the Await code.
AFAIK, there is a limitation of 60 concurrent threads in OTL when using the threadpool.
I am working on the application which has two listboxes.I load the two listboxes with values and when i keep on clicking the items from the list box i get the following error while debugging.
Running the exe causes the application to close.Sometimes i get the "Access Violation" message.
so what should I do to get rid of this error from my aaplication?
EDIT
..
The main form has timer that refresh all the controls
timer_RefreshCOntrol (intervali 1).
whenver the editBox_one is modified(value)
this function is called
Procedure TStringSetting.SetValue (const AValue : String);
Begin
...
If FValueControl <> Nil then
Begin
FValueControl.OnChange := VoidNotifyEvent;
FValueControl.Text := NewValue;
FValueControl.OnChange := EditChange; //<--here the stackoverflow error comes....
end;
end;
Procedure EditChange (Sender: TObject);
Begin
Value := FValueControl.Text;
If Not EditIsValid then FValueControl.Font.Color := clRed
else If Dirty then FValueControl.Font.Color := clBlue
else FValueControl.Font.Color := clWindowText;
If #OldCustomEditChange <> Nil then OldCustomEditChange(Sender);
end;`
the EditChange (Sender: TObject); <--keeps geting called and the stackoverflow error comes
EditChange is assigned to the editbox on FormCreate
EDIT2
I am not the original developer.I just handled code sometimes back, major refactoring is not possible.
edit 3
The call stack value but what is the "???"
EDIT 4
after going through #Cosmin Prund and #david
i got the place where the infinity call start
Procedure TFloatSetting.EditChange (Sender: TObject);
Begin
SkipNextOnChange := True;
Inherited EditChange(Sender);
IfValidThenStore(FValueControl.Text);
Inherited EditChange(Sender); {<-------This is where it start}
end;
Procedure TStringSetting.EditChange (Sender: TObject);
Begin
Value := FValueControl.Text;
If Not EditIsValid then FValueControl.Font.Color := clRed
else If Dirty then FValueControl.Font.Color := clBlue
else FValueControl.Font.Color := clWindowText;
If #OldCustomEditChange <> Nil then OldCustomEditChange(Sender); {<---this keeps calling Procedure TFloatSetting.EditChange (Sender: TObject);}
end;
Based in the posted call stack it's obvious why the error is happening: TStringSetting.EditChange triggers TFloatSetting.EditChange and that in turn triggers TStringSetting.EditChange. The loop goes on like this until all stack space is exhausted.
Here are some tips on why that might happen, and tips on how to debug and fix it:
Maybe the controls involved trigger the OnChange event handler when the Value is changed progrmatically. If the two editors are supposed to display the same data in two formats and you're using the respective OnChange event handlers to keep them in sync, this might be the cause.
Maybe you're directly calling one event handler from the other.
Ways to debug this:
You should first try the breakpoint solution, as suggested by paulsm4. If the stack overflow happens every time one of the OnChange handlers is called, this solution would easily work.
Comment-out the code for one of the event handlers. Run the program, the error should no longer appear. Un-comment the code in tiny (but logical) amounts, test and repeat. When the error shows up again, you know you fund the line that's causing the error. If you can't figure it out yourself, edit the question, add the code and mark the line that you just found out it's giving you trouble.
If the controls you're using are triggering the OnChange event handler when there value is changed programatically, you should make your event handlers non-reentrant: that would stop the infinite recursive loop for sure. I almost always assume controls trigger OnChange or equivalent events when properties are changed from code and always protect myself from re-entry using something like this:
// Somewhere in the private section of your form's class:
FProcessingEventHandler: Boolean;
// This goes in your event handler
procedure TYourForm.EventHandler(Sender:TObject);
begin
if FProcessingEventHandler then Exit; // makes code non-reentrant
FProcessingEventHandler := True;
try
// old code goes here ...
finally FProcessingEventHandler := False;
end;
end;
Suggestions:
Set a breakpoint in EditChange and OldCustomEditChange to see who's calling them. Each invocation. Clearly, only EditChange should ever call OldCustomEditChange.
Look in your .dfm to make sure EditChange is only assigned to one event (not multiple events) and OldCustomEditChange isn't assigned at all.
You report a non-terminating recursive call sequence to EditChange. Looking at the code of EditChange there are two candidates for a recursive call:
OldCustomEditChange being equal to EditChange, or calling a function that in turn calls EditChange.
An event handler that responds to changes to FValueControl.Font by calling EditChange.
These are the only opportunities for the code in EditChange to call itself.
It is easy to see how both of these possibilities leads to the non-terminating recursive function call and eventually the stack overflow. Of the two candidates my bet is number 1. I would study carefully what happens when OldCustomEditChange is called.
To debug a stack overflow of this nature simply open the call stack window and look at the long sequence of calls. You will typically see a pattern with one function calling itself, possibly via one or more intermediate functions.
please tell me: how to know if TEvent is Signaled or not?
Click on STOP-button = SetEvent(Events[1]);
I am trying to unzip an archive and if STOP-button is pressed then a tread must be terminated and Unzippping must be aborted.
My code:
procedure TForm2.ZipForge1OverallProgress(Sender: TObject; Progress: Double;
Operation: TZFProcessOperation; ProgressPhase: TZFProgressPhase;
var Cancel: Boolean);
begin
if Events[1]<>null then
begin
ThreadUpdating.Terminate;
Abort;
end else
form2.Update_ProgressBar.Position := Trunc(Progress);
end;
But if I press STOP-button(SetEvent(Events[1])) nothing happens.
PS: I am using WaitForMultipleObjects(Event[1],Event[2]) in a thread. Event [1] is being used as a signal of STOP in two parts: in ZipForge1OverallProgress and WaitForMultipleObjects.
Call WaitForMultipleObjects, but do it properly. You haven't shown that code, and the code you have shown doesn't look right anyway.
First, it looks like you're trying to check whether the Events[1] element is a null pointer. Null pointers in Delphi are spelled nil, not null; the latter is a function that returns a null Variant value (but since Variant is convertible to lots of other types, the compiler probably doesn't alert you that your code is wrong). Next, it looks as though the event you're handling has a Cancel parameter that you can set to notify the caller that it should stop what it's doing, but instead of just setting that, you're throwing an EAbort exception.
If the progress event you show here is really running in a separate thread, then it must not modify property of VCL objects like TProgressBar. You need to use Synchronize to make sure VCL operations only occur in the VCL thread.
As I said, you need to call WaitForMultipleObjects property. That means passing it four parameters, for one thing. You appear to have an array with at least two handles in it, so call it like this:
var
Ret: DWord;
Ret := WaitForMultipleObjects(2, #Events[1], False, Timeout);
case Ret of
Wait_Object_0: begin
// Events[1] is signaled
end;
Wait_Object_0 + 1: begin
// Events[2] is signaled
end;
Wait_Timeout: begin
// Neither is signaled. Do some more work, or go back to waiting.
end;
Wait_Failed: begin
RaiseLastOSError;
end;
end;
If all you want to do is check whether the handle is signaled, but you don't want to wait for it to become signaled if it isn't already, then use a timeout value of zero.
'if Events[1]<>null then begin' is this pseudocode? Doesn't lok like it - looks more like real Delphi to me:) If so, you are just checking to see if the Event object is assigned, rather than signaled.
If you want to poll the stop event in your OverallProgress handler, you need to call WaitForSingleObject() with a timeout of 0.
Can you not just check a 'stop' boolean in your handler? This would be much quicker than a kernel call. You may need the Event as well so that the WFMO call at the top of the thread gets signaled when an abort/terminate is needed or you might get away with signaling some other event in the WFMO array by always checking for stop:
TmyThread = class(TThread)
..
public
stopRequested:boolean;
procedure stop;
..
end;
procedure TmyThread.stop;
begin
stopRequested:=true;
someEventInWFMOarray.signal;
end;
procedure TmyThread.execute;
begin;
while true do
begin
waitForMultipeObjects();
if stopRequested then exit;
work;
end;
end;
TForm2.ZipForge1OverallProgress(sender:TObject,......)
begin
cancel:=TmyThread(Sender).stopRequested;
if cancel then exit;
doStuff;
end;