NAME
ProgressBar::Stack - Progress bar implementation with stack support and useful loop wrappers
SYNOPSIS
use ProgressBar::Stack;
init_progress;
sleep(1);
update_progress 20;
sleep(2);
update_progress 60;
sleep(2);
update_progress 100;
print "\n";
init_progress(message => "Calculating");
my $sum = 0;
for_progress {
$sum+=$_;
sleep(1);
} 0..10;
print "\nSum = $sum\n";
DESCRIPTION
ProgressBar::Stack
creates a convenient framework for adding progress bars to long processes. Sometimes you have long process which consists of several subprocesses, some of which have cycles (including nested ones), some called several times and so on. If you want to display continuous progress bar from 0% to 100% for such complex process, you will have bad times calculating current percentage for each subprocess. ProgressBar::Stack
does much of dirty work for you.
Note that ProgressBar::Stack
provides only simple console renderer of current progress. If you want to use it in some GUI application, you should write your own renderer and pass it to init_progress
(see below).
There are two interfaces provided: one is object-oriented, the other is not. Non-OO interface actually creates single object and delegates all calls to it. Practically using non-OO interface is enough in many cases, especially taking into account that different threads will have independent progress bars, but for some windowed applications several progress bars might be necessary.
Non-OO interface
All functions of non-OO interface are exported by default.
- init_progress %parameters
-
Initializes progress bar and updates it to 0%. Parameters (all optional) include:
- message
-
Default message describing the action performed. This will be passed to renderer and displayed to the user. Can be overridden later by
update_progress
calls.Default value: empty string.
- count
-
Maximum value for your progress bar. This takes effect when you call
update_progress
orsub_progress
. Example:init_progress(count => 2); sleep(1); update_progress(1); # means half of process is done sleep(2); update_progress(2); # means whole process is done
Default value: 100.
Actually it's better not to use this parameter at all always scaling your progress bar from 0 to 100.
- renderer
-
Subroutine to be called when progress bar should be updated. Note that calling
update_progress
doesn't mean thisrenderer
will be called for sure.update_progress
may suppress calls to therenderer
in order not to update progress bar too fast.renderer
receives three parameters:$value
,$message
and$progress
.$value
is float value between 0 and 100 (regardless ofcount
parameter) which represents current progress.$message
is supplementary message describing current action.$progress
is progress bar object, which you can use to access some advanced parameters. For example if you want to calculate estimated time, you can use $progress->{starttime} to get time when the process started. See alsorunning_time
,remaining_time
andtotal_time
.Default renderer provides simple console output like this:
[##### ] 25.0% ETA: 0:05 Message
- minupdatetime
-
Time in seconds during which updates of progress bar (
renderer
calls) are disabled unless message changed, progress bar changed more thanforceupdatevalue
(see below) or reached 100%.Default value is 0.1.
- minupdatevalue
-
Progress bar update will be disabled if difference between current and previous value less than this parameter unless message changed or progress bar reached 100%.
Default value is 0.1.
- forceupdatevalue
-
Progress bar update will be enabled if difference between current and previous value exceeds this parameter even if
minupdatetime
haven't passed yet.Default value is 1.
- update_progress VALUE, MESSAGE
- update_progress VALUE
- update_progress
-
Inform progress bar that it should be updated to value
VALUE
and message should be changed toMESSAGE
. IfMESSAGE
is omitted, last message on current stack level will be used:init_progress; update_progress 0, "Outside"; sleep 1; update_progress 20; # "Outside" message will be used sleep 1; sub_progress { update_progress 0, "Inside"; sleep 1; update_progress 50; # "Inside" message will be used sleep 1; } 70; sleep 1; update_progress 80; # "Outside" message will be used again
If VALUE is omitted, then maximal value will be used (specified by
count
ininit_progress
, 100 by default). Progress bar will be updated for sure if it reached 100% or message changed since last time. Otherwise actual update (call torenderer
) may not be performed depending onminupdatetime
,minupdatevalue
andforceupdatevalue
parameters (seeinit_progress
). - sub_progress BLOCK, VALUE
-
Pushes current progress bar range and message to the stack, shortens range to
[curvalue, VALUE]
(wherecurvalue
determined by the latestupdate_progress
call), evaluates block, callsupdate_progress
and pops current state back. This function lets you defining subprocesses, inside which you can use whole range [0, 100] inupdate_progress
calls as for top-level process. Example:init_progress; # This subprocess uses [0, 50] progress bar range sub_progress { sleep 2; # 20% will be displayed, because we're inside subprocess update_progress 40; sleep 2; # 40% will be displayed, because we're inside subprocess update_progress 80; sleep 1; # note that at the end of subprocess update_progress # is called automatically, thus 50% will be displayed } 50; # This subprocess uses [50, 100] progress bar range sub_progress { sleep 1; # 75% update_progress 50; sleep 1; # 100% will be displayed automatically } 100;
In general any call of function, which works long enough to update progress by its own, should be wrapped into
sub_progress
, because function should not care whether it's top-level process or part of any subprocess:# Pass of some long process sub pass() { update_progress 0, "Performing pass"; sleep(1); update_progress 50; sleep(1); update_progress 100; # just for the case it's top-level process } # Process consisting of two passes: init_progress; sub_progress {pass} 50; # will display 25%, then 50% sub_progress {pass} 100; # will display 75%, then 100%
Of course
sub_progress
can be unlimitedly nested. Example:init_progress; sub_progress { sub_progress { update_progress 0, "First step of first step"; sleep(1); update_progress 50; # 10% displayed sleep(1); } 40; sub_progress { update_progress 0, "Last step of first step"; sleep(1); update_progress 50; # 35% displayed sleep(1); } 100 } 50; sub_progress { update_progress 0, "Last step"; sleep(1); update_progress 50; # 75% displayed sleep(1); } 100;
If
BLOCK
returns value, it will be returned bysub_progress
. - for_progress BLOCK, LIST
-
Evaluates
BLOCK
for each element fromLIST
, loading its elements consequently into$_
. For each iterationsub_progress
is called reducing the progress bar range to appropriate part assuming that each iteration takes the same time. At the end of iterationupdate_progress
is called automatically. You can usenext
andlast
as in normalfor
cycle. Example:init_progress; for_progress { sleep 1; } 1..10;
In this example progress bar will display 10%, 20% and so on till 100%.
Inside
BLOCK
you can callupdate_progress
changingVALUE
from 0 to 100, which represents progress of current iteration:init_progress; for_progress { update_progress(0, "Processing $_"); sleep 1; update_progress(50, "Processing $_"); sleep 1; } qw(Banana Apple Pear Grapes);
You will see the following sequence of progress bar updates:
[ ] 0.0% ETA: ?:?? Processing Banana [## ] 12.5% ETA: 0:06 Processing Banana [##### ] 25.0% ETA: 0:05 Processing Banana [##### ] 25.0% ETA: 0:05 Processing Apple [####### ] 37.5% ETA: 0:04 Processing Apple [########## ] 50.0% ETA: 0:03 Processing Apple [########## ] 50.0% ETA: 0:03 Processing Pear [############ ] 62.5% ETA: 0:03 Processing Pear [############### ] 75.0% ETA: 0:02 Processing Pear [############### ] 75.0% ETA: 0:02 Processing Grapes [################# ] 87.5% ETA: 0:01 Processing Grapes [####################] 100.0% ETA: 0:00 Processing Grapes
Of course nested loops work fine also:
init_progress; for_progress { for_progress { sleep 1; } 1..$_; } 1..5;
Note that this progress bar will become slower to the end as
for_progress
assumes each iteration takes the same time, but latter iterations of outerfor_progress
are obviously slower. - map_progress BLOCK, LIST
-
Similar to
for_progress
but works likemap
returning list of processed elements:init_progress(); my @lengths = map_progress { sleep(1); length($_); } qw(Banana Apple Pear Grapes);
- reduce_progress BLOCK, LIST
-
Similar to
for_progress
but works likeList::Util::reduce
returning accumulated value:init_progress(minupdatevalue => 1); print "\nSum of cubes from 1 to 1000000 = ".reduce_progress {$a + $b*$b*$b} 1..1000000;
Note that this works much slower than simple
List::Util::reduce
(about 4-5 times as measured). Thus use carefully in cases when single iteration is very short. You may consider optimizing the process decomposing the loop into two nested ones and using progress for outer only like this:use List::Util qw(reduce); init_progress; print "\nSum of cubes from 1 to 1000000 = ".reduce {$a + $b} map_progress {reduce {$a + $b} map {$_*$_*$_} $_*1000-999..$_*1000} 1..1000;
- file_progress BLOCK, FH
-
Similar to
for_progress
but reads text file by given filehandleFH
line by line. Progress range is based on current offset inside the file and file size. Thus filesize should be known for this filehandle. Example:init_progress; open(F, "test.txt") || die "$!"; my $nbytes = 0; file_progress { $nbytes+=length($_); sleep(1); } \*F; print "\nLength = $nbytes\n";
- push_progress START, END
-
Low-level function to put new progress range into stack. Also the last message is saved there. Generally you shouldn't use it unless you extend capabilities of this module.
- pop_progress
-
Low-level function to remove current progress range from stack, activating previous progress range and message. It will
die
if you call it on empty stack. Generally you shouldn't use it unless you extend capabilities of this module.
Object-oriented interface
Object-oriented interface is pretty similar to subroutine interface described above. To get the progress bar object, instead of init_progress
you should call new ProgressBar::Stack
(parameters are the same). All methods of this object are the same as functions above, but without suffix '_progress' in the title (update
, sub
, for
, map
, reduce
, file
, push
and pop
). Parameters are the same except that first parameter is the object. Thus, one of above examples may be rewritten as following:
my $p = new ProgressBar::Stack;
$p->for(sub {
$p->for(sub {
sleep 1;
}, 1..$_);
}, 1..5);
- running_time
- remaining_time
- total_time
-
These methods return running time (more precisely, time between
new
orinit_progress
call and the latest call of the renderer), estimated remaining time and estimated total time. All times are in seconds, float numbers (Time::HiRes
is used internally). These methods have no non-OO counterparts as they should be used inside renderer only where object is always available as third parameter. You may use them like this:init_progress(renderer => sub { $progress = $_[2]; print "Remaining time: ".$progress->remaining_time()."\n"; });
Estimated time calculation simply divides running time by current progress value, so estimation will be incorrect if process speed changes significantly. When estimation cannot be calculated (progress is still at 0%)
remaining_time
andtotal_time
return -1.
COPYRIGHT
Copyright (c) 2009-2010 Tagir Valeev <lan@nprog.ru>. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.