开发者

Display progress from time consuming process

Sorry for my bad English... Using Delphi 7 I want to create a开发者_开发知识库 dialog window to show that something is happening in my application when i have to run slow processes. My idea was to do something that i can use like:

with TMyDialog.Create do
begin
  //call the time consuming method here
  Free;
end;

When i create the dialog, a window with an animation or something will show and will disappear after the time consuming method ends (on the free method) - it would be nice if I could manually update the progress from that dialog, in cases when the process give me such information:

with TMyDialog.Create do
begin
  while time_consuming_method do
  begin
    UpdateStatusOnMyDyalog();
  end;
  Free;
end;

but normally it would only be a animation to show that something is happening.

Has someone did something like that, knows a component or have any suggestions on whats the best way to do it in the most clean and simple way?


The bad but easy way to do this is to call Application.ProcessMessages or UpdateWindow(Handle) (to update the form) and increment a progressbar during your time_consuming_method. A slightly better method would be to wrap your time_consuming_method up into a class with an OnProgress event. Finally as other people have suggested you could use a separate thread for your time_consuming_method - which is the most powerful technique, but has the worst learning curve.


You will need to run your time consuming process in a separate thread, and have that thread report its' progress to your main UI thread using synchronization.

Here is an example that shows you how to start a new thread and have that thread do the synchronized progress reporting.

--jeroen


It's quite common to report progress in this way (using, for instance, a progress bar).

Your "time consuming process" needs to receive either a callback function that will be called every time it has some progress to report or, if you are willing to bind it more tightly with your user interface design, a reference to a component of some kind that it will know how to update. This can be a progress bar which it will step, a listbox or memo field that will receive a new line with status updates, a label control the caption of which will get updated, and so on.


Displaying a progress during long operations depend on several factors (limitations) :

  • Defined/undefined progress (you know, may calculate, how many steps does the operation take)
  • Interruptibility/segmentation (you will be able, or have to, interrupt the operation to refresh the progress to the user)
  • The operation is thread-able (you may put the operation a thread)

For defined progress it's common to display a segmented progress bar, and for undefined an animation or progress bar with the style "Marquee".

The main consideration is whether the operation is segmented/interruptible or not. Because if it's not, and you don't take care of it, your application will freeze until the operation finishes. Searching for files is one example of segmented operation. Each found file is one segment , and it gives you the ability to display the progress to the user, and refresh the display.

Example:

TFrmUndefinedProgress = class(TForm)
private
   FCallbackProc           : TNotifyEvent;
protected
   procedure WndProc(var Message:TMessage); override;
public
   constructor Create(aCallbackProc: TNotifyEvent);
  procedure UpdateProgress(const aStr : string; aPercent : integer); 
...
constructor TFrmUndefinedProgress.Create(aCallbackProc: TNotifyEvent);
begin
   inherited Create(nil);
   FCallbackProc     := aCallbackProc;
end;
...
procedure TFrmUndefinedProgress.FormShow(Sender: TObject);
begin
   Update;
   PostMessage(Handle, WM_START_UNDEFPROG, 0, 0); 
end;

Send message to window procedure on your form's OnShow, to make sure that it will be rendered first.

procedure TFrmUndefinedProgress.WndProc(var Message: TMessage);
begin
   if (Message.Msg = WM_START_UNDEFPROG) then begin
      if Assigned(FCallbackProc) then
         FCallbackProc(Self); --> Call your callback procedure
   PostMessage(Handle, WM_CLOSE, 0, 0); --> close when finished
   end
   else
   inherited;
end;

And if you make a regular procedure in your form's unit...

procedure ShowUndefinedProgress(aCallbackProc : TNotifyEvent);
var
   FrmUndefinedProgress : TFrmUndefinedProgress;
begin
   FrmUndefinedProgress := nil;
   try
      FrmUndefinedProgress         := TFrmUndefinedProgress.Create(aCallbackProc);
      FrmUndefinedProgress.ShowModal;
   finally
      FreeAndNil(FrmUndefinedProgress);
   end;
end;

You then may call progress form like this:

ShowUndefinedProgress(HandleSomeOperation);

where you pass your aCallbackProc. Inside you put your operation:

procedure TForm1.HandleSomeOperation(Sender: TForm);
var
   aProgress : TFrmUndefinedProgress;
begin
   --> Do something
   aProgress := TFrmUndefinedProgress(Sender);
   aProgress .UpdateProgress(SomeMessage, Percent);

Update the display for each found file ...

If you have operation that takes long time, but you have no way of interrupting it, then you should put it in a thread.

  • Create a descendant of the TThread object.
  • Override it's Execute method
  • Do your thing inside Execute And use it:
  • Create a form
  • Start some animation on it's OnShow
  • Then run your thread
  • Close when thread finishes.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜