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_progresscalls.Default value: empty string.
- count
-
Maximum value for your progress bar. This takes effect when you call
update_progressorsub_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 doneDefault 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_progressdoesn't mean thisrendererwill be called for sure.update_progressmay suppress calls to therendererin order not to update progress bar too fast.rendererreceives three parameters:$value,$messageand$progress.$valueis float value between 0 and 100 (regardless ofcountparameter) which represents current progress.$messageis supplementary message describing current action.$progressis 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_timeandtotal_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 (
renderercalls) 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
minupdatetimehaven'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
VALUEand message should be changed toMESSAGE. IfMESSAGEis 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 againIf VALUE is omitted, then maximal value will be used (specified by
countininit_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,minupdatevalueandforceupdatevalueparameters (seeinit_progress). - sub_progress BLOCK, VALUE
-
Pushes current progress bar range and message to the stack, shortens range to
[curvalue, VALUE](wherecurvaluedetermined by the latestupdate_progresscall), evaluates block, callsupdate_progressand pops current state back. This function lets you defining subprocesses, inside which you can use whole range [0, 100] inupdate_progresscalls 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_progresscan 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
BLOCKreturns value, it will be returned bysub_progress. - for_progress BLOCK, LIST
-
Evaluates
BLOCKfor each element fromLIST, loading its elements consequently into$_. For each iterationsub_progressis called reducing the progress bar range to appropriate part assuming that each iteration takes the same time. At the end of iterationupdate_progressis called automatically. You can usenextandlastas in normalforcycle. Example:init_progress; for_progress { sleep 1; } 1..10;In this example progress bar will display 10%, 20% and so on till 100%.
Inside
BLOCKyou can callupdate_progresschangingVALUEfrom 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 GrapesOf 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_progressassumes each iteration takes the same time, but latter iterations of outerfor_progressare obviously slower. - map_progress BLOCK, LIST
-
Similar to
for_progressbut works likemapreturning 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_progressbut works likeList::Util::reducereturning 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_progressbut reads text file by given filehandleFHline 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
dieif 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
neworinit_progresscall and the latest call of the renderer), estimated remaining time and estimated total time. All times are in seconds, float numbers (Time::HiResis 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_timeandtotal_timereturn -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.