Jump to content

Displaying progress during time-consuming operations


Tim

Recommended Posts

Hi,
 
I am writing a wizard-style application which allows customers to create orders. When the user clicks on the Submit Order button on the last page of the wizard, a series of webservice calls are performed, which can take several seconds in total to complete. I would like to update the display after each webservice call. It seems however that the user interface is not updated until the button's OnClick event handler finishes executing. For example, the following code results in the strings 'one', 'two', 'three', 'four' being written to the memo control all at once after a four-second delay:
 
procedure TMainForm.UniButton1Click(Sender: TObject);
begin
  sleep(1000);
  UniMemo1.Lines.Add('one');
  sleep(1000);
  UniMemo1.Lines.Add('two');
  sleep(1000);
  UniMemo1.Lines.Add('three');
  sleep(1000);
end;
 
Is there some way of updating the display in the middle of an event handler? I tried calling UniSession.AddJS('alert("one")') in place of UniMemo1.Lines.Add('one'), but this also doesn't seem to take effect until the event handler is finished. I also tried writing the progress messages to a form variable in TMainForm.UniButton1Click and then polling this variable with a Timer, but changes made to the variable's value don't seem to be visible in the OnTimer event until TMainForm.UniButton1Click is finished.
 
Maybe I need to perform the webservice calls in a background thread, and/or write some javascript code that regularly polls for progress messages?

 

I would be grateful for any insights.

 

Thanks,

Tim

Link to comment
Share on other sites

Thanks Zilav for the reply.
 
So you mean something like the following code?
 
type
  TWorkerThread = class(TThread)
    procedure Execute();  override;
  end;


var
  Gv_ProgressMessage : string;
  Gv_ProgressMessageCriticalSection : TRTLCriticalSection;


procedure TWorkerThread.Execute();


  procedure WriteProgressMessage(AMessage : string);
  begin
    EnterCriticalSection( Gv_ProgressMessageCriticalSection );
    try
      Gv_ProgressMessage := AMessage;
    finally
      LeaveCriticalSection( Gv_ProgressMessageCriticalSection );
    end;
  end;


begin
  sleep(1000);
  WriteProgressMessage('one');
  sleep(1000);
  WriteProgressMessage('two');
  sleep(1000);
  WriteProgressMessage('three');
  sleep(1000);
end;


procedure TMainForm.UniButton3Click(Sender: TObject);
var
  WorkerThread : TWorkerThread;
begin
  WorkerThread := TWorkerThread.Create();
end;


procedure TMainForm.UniTimer1Timer(Sender: TObject);
begin
  EnterCriticalSection( Gv_ProgressMessageCriticalSection );
  try
    if Gv_ProgressMessage <> '' then begin
      UniMemo1.Lines.Add( Gv_ProgressMessage );
      Gv_ProgressMessage := '';
    end;
  finally
    LeaveCriticalSection( Gv_ProgressMessageCriticalSection );
  end;
end;


function MainForm: TMainForm;
begin
  Result := TMainForm(UniMainModule.GetFormInstance(TMainForm));
end;


initialization
  Gv_ProgressMessage := '';
  InitializeCriticalSection( Gv_ProgressMessageCriticalSection );
  RegisterAppFormClass(TMainForm);


finalization
  DeleteCriticalSection( Gv_ProgressMessageCriticalSection );

 

Or is there maybe a simpler way to do it?

 

Link to comment
Share on other sites

Thanks very much for the example!

 

So it is necessary to perform time-consuming operations in a background thread if one wants to display the progress to the user.

 

It was interesting how the updating of the progress bar is performed in the example using AddJS:



    UniSession.AddJS(Format('MainForm.pg.updateProgress(%d/%d, ''Running...'', true);', [job.Progress, 100]));


Instead of setting the control's property directly:



    pg.Position := job.Progress;


I tried using the second way, but then the progress bar advances jerkily. So I'm guessing this creates more HTTP traffic or something.

 

By the way -- how did you know about the updateProgress function? Is there some documentation or special place in the sourcecode where one can look up the javascript functions available for each control?

 

Thanks again,

Tim

Link to comment
Share on other sites

The only difference is that js method sets the last parameter to True which means "animate". Normally UniGUI exposes majority of properties in designer but "Animated" is not there yet. If you don't need animation, then just setting Position is easier. Traffic is the same.

You can aways check Sencha ExtJS docs for javascript methods and properties on client side. For this particular function

http://docs.sencha.com/extjs/4.2.3/#!/api/Ext.ProgressBar-method-updateProgress

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...