Jump to content
Tommi Prami

Lazy loading progressbar dialog

Recommended Posts

Yo,

As windows does the similar thing when doing file operations in explorer, if it start to take time, then it bring s dialog up, and do not flash it if operation is fast (At least sometimes it seems this way).

I've been pondering this kind of pattern for a long time.  Never tried to code this, but as concept it might be nice, but I have feeling that this is easy to mess up, and for sure easy to kiss some corner cases to have super weird issues, I think.

My idea would be something like

LLazyProgress := LAzyProgressFactory(ParentFormEtc, lpMarquee, 300);
try
  ... Work
finally
  LLazyProgress.Free;
end;

If this would be network or file operation, sometimes it might be fast and sometimes very very slow,. And you kind of can't know for sure.,

My idea was that there would be some, like 300ms delay, if process takes more time than that, progress dialog would show, 

It could be marquee style, or if progress is more defined and known, could pass some callback for the actual progress and show it,

Any thoughts/ideas on this. Mainly if problem with fluctuation of time process takes, sometimes there would be fast flash of dialog for user, and they never could see what was it. Completely different discussion should there be some kind of UI element to show user that progress has finished.

 

-Tee-

Share this post


Link to post

I like these messages that some use instead of a progress bar.  A progress bar is linear - while the messages don't necessarily imply a linear time flow.
Getting started
Putting the ducks in a row
Cleanup on isle four
Seeing our target

Just one more thing
We have arrived

Share this post


Link to post
32 minutes ago, Lars Fosdal said:

I like these messages that some use instead of a progress bar.  A progress bar is linear - while the messages don't necessarily imply a linear time flow.
Getting started
Putting the ducks in a row
Cleanup on isle four
Seeing our target

Just one more thing
We have arrived

I most likely would have more than less both, but slightly off topic...

Share this post


Link to post

Design wise, it depends a lot on whether your app can keep working while progress is ongoing or not.


In some places - I just keep a panel hidden on top of the main window and expand it when I need to alert the user to why the app is blocking/busy.

The content of that can be messages, a checklist, or a progress bar.  

Here a launch message "Forbereder oppstart av TineAdmin for Brummundal Test" (Preparing startup of Tine Admin for ...) after the user clicked the TineAdmin icon on the toolbar.
This app is a little bit unusual as it doesn't have a main menu - but when it does - and I am blocking - I disable and update it.
image.thumb.png.87e8d7b38b6b447d9a945f0833ad135d.png
The benefit of using a panel is that it doesn't interfere with the window functionality when it the operation is non blocking.

In other cases, I have a floating window on top with some messages.
Here for login progress (Checking version)
image.thumb.png.58978c50cb3273c9a23dc252d572711b.png

Share this post


Link to post
6 hours ago, Tommi Prami said:

Yo,

As windows does the similar thing when doing file operations in explorer, if it start to take time, then it bring s dialog up, and do not flash it if operation is fast (At least sometimes it seems this way).

I've been pondering this kind of pattern for a long time.  Never tried to code this, but as concept it might be nice, but I have feeling that this is easy to mess up, and for sure easy to kiss some corner cases to have super weird issues, I think.

My idea would be something like

LLazyProgress := LAzyProgressFactory(ParentFormEtc, lpMarquee, 300);
try
  ... Work
finally
  LLazyProgress.Free;
end;

If this would be network or file operation, sometimes it might be fast and sometimes very very slow,. And you kind of can't know for sure.,

My idea was that there would be some, like 300ms delay, if process takes more time than that, progress dialog would show, 

It could be marquee style, or if progress is more defined and known, could pass some callback for the actual progress and show it,

Any thoughts/ideas on this. Mainly if problem with fluctuation of time process takes, sometimes there would be fast flash of dialog for user, and they never could see what was it. Completely different discussion should there be some kind of UI element to show user that progress has finished.

 

-Tee-

Relocate the actual work to a secondary thread. At the start of the thread's Execute method use TThread.Queue to pass a method to the main thread that creates the progress dialog. The dialog is not shown immediately, though, it just starts a timer with the delay you want. If the timer fires before the thread has completed its work, show the dialog. The dialog can the either use a timer to check for the thread's progress at intervals to update its display, or the thread can inform the dialog through Synchronized method calls of its progress. When done it can then tell the dialog to close itself, also through a Synchronized method call.

  • Like 1

Share this post


Link to post
17 hours ago, PeterBelow said:

Relocate the actual work to a secondary thread. At the start of the thread's Execute method use TThread.Queue to pass a method to the main thread that creates the progress dialog. The dialog is not shown immediately, though, it just starts a timer with the delay you want. If the timer fires before the thread has completed its work, show the dialog. The dialog can the either use a timer to check for the thread's progress at intervals to update its display, or the thread can inform the dialog through Synchronized method calls of its progress. When done it can then tell the dialog to close itself, also through a Synchronized method call.

Relocating actual work to thread, is what I would like to avoid, as it adds quite log extra complexity, like needs new connection to database etc. This why I asked because there might not be good solution without having actual work in a separate thread/task (= thread). For sure having the actual work in separate thread would have its own benefits, like they would crash if they mess around with GUI, but most likely work in legacy app has some GUI updates, that's why it would make it much easier to just keep the work in main thread.

So far what I've been pondering that it would need thread, but been thinking that maybe the Lazy progress bar showing logic would be in thread.

I thought something like this could work:

1. Have and initialize background thread which waits for progress bar dialog requests.
2. Also create initialize progress bar dialog 
3. Request progress bar 
  3a If progress bar request is still alive and time period is over, show dialog

4. when work is done, ask thread to hide the dialog, if visible at all

5. free the request

Needs some thread safety and TEvents or something for signaling and so on. Also Progress bar dialog would naturally be modal, and process messages in a way or other.


 

Share this post


Link to post

https://bitbucket.org/anders_melander/better-translation-manager/src/master/Source/amProgress.API.pas

and another, slightly older, version of the same:

https://bitbucket.org/anders_melander/dwscriptstudio/src/master/Source/amProgress.pas

and even a DWScript wrapper (the second & third screenshots are actually from a DWScript):

https://bitbucket.org/anders_melander/dwscriptstudio/src/master/Source/ScriptRTL/amScriptModuleUserInterfaceProgress.pas

 

image.png.530ce6917587137777be698047e7618e.png

  • Displays a non-modal form with a progress bar, a status message, and an optional cancel button.
  • Defer the initial display of the progress form (what you call lazy loading). Default delay is 500 mS.
  • Limit rate of progress update to minimize UI overhead. Default is max 1 update per 100 mS.
  • Selectively pumps message queue to avoid application freeze and enable user to move/cancel progress dialog during use.
  • Progressive or marquee mode.

The current implementation uses DevExpress label and button controls but these can just be replaced with regular VCL controls without any loss of functionality.

 

Usage:

var Progress := ShowProgress('Hello world', False);
Progress.EnableAbort := True;

Progress.Progress(psBegin, 0, 100, 'Charging flux capacitor...');

for var i := 0 to 100 do
begin
  Sleep(100);
  Progress.AdvanceProgress;
end;

image.png.5f833449b68c0ac6c63c33dc77709502.png

 

and in Marquee mode:

var Progress := ShowProgress('Hello world', False);
Progress.EnableAbort := True;
Progress.Marquee := True;

Progress.UpdateMessage('Charging flux capacitor...');

while (not Progress.Aborted) do
begin
  Sleep(100);
  Progress.AdvanceProgress;
end;

image.thumb.png.e77a4b6643d179edb15e494686db6667.png

  • Like 5

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×