leocharre has asked for the wisdom of the Perl Monks concerning the following question:

Some cli utilities will have a meter, a "task completed" progress bar.. like..

<==    >
then it changes to 
<===   >
then
<====  >

How the heck do they do that?? Is that the screen redrawn, it's a character deleted/appended from the current shell screen?? Same method could be used to do an animated rotating bar.. right ? - \ | / - \ ...
Any hints for where to look? :)

( thank you so much for all the help!!!!!! )

  • Comment on "percent task completed progress bar" in cli interface, how?

Replies are listed 'Best First'.
Re: "percent task completed progress bar" in cli interface, how?
by Fletch (Bishop) on Jun 12, 2006 at 14:54 UTC

    CPAN is your friend: Term::ProgressBar. It's also trivial to roll your own with \r (or not so trivially but possibly more robust with Term::Cap).

Re: "percent task completed progress bar" in cli interface, how?
by zentara (Cardinal) on Jun 12, 2006 at 15:43 UTC
    Update. Fixed sprintf format. Thanks booger

    This is probably what you want, a wget-style progressbar.

    #!/usr/bin/perl $|=1; do{ print progress_bar( $_, 100, 25, '=' ); select(undef,undef,undef, .1) } for 1..100; # figure out how to work it in to your program sub progress_bar { my ( $got, $total, $width, $char ) = @_; $width ||= 25; $char ||= '='; $num_width = length $total; sprintf "|%-${width}s| Got %${num_width}s bytes of %s (%.2f%%)\r", $char x (($width-1)*$got/$total). '>', $got, $total, 100*$got/$ +total; }

    I'm not really a human, but I play one on earth. flash japh
      Your line

      sprintf "|%-${width}s| Got %${num_width}s bytes of %s (%.2f%)\r",

      should really read

      sprintf "|%-${width}s| Got %${num_width}s bytes of %s (%.2f%%)\r",

      Cheers, Matt

        You may know more about sprintf's formats than me, but adding an extra % , as you suggest, dosn't change the behavior at all. Can you explain why?

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: "percent task completed progress bar" in cli interface, how?
by Joost (Canon) on Jun 12, 2006 at 15:19 UTC
Re: "percent task completed progress bar" in cli interface, how?
by johngg (Canon) on Jun 12, 2006 at 15:28 UTC
    This routine returns a code reference that does an animated rotating bar. If you give it a numeric argument of, say, 25 it will only turn the bar on the the 25th, 50th etc. invocation. It's pretty crude having been written when I was new to Perl.

    sub init_tumble { my $every = shift || 0; my(@tumble) = ('-', '\\', '|', '/'); my $tsub = 0; my $count = 0; my $rs = sub { $count++; $saved_autoflush = STDOUT->autoflush; if($every) { return if $count % $every; } STDOUT->autoflush(1); print "$tumble[$tsub++]\b"; $tsub = 0 if $tsub > 3; STDOUT->autoflush($saved_autoflush); }; return $rs; }

    You would call it like this

    my $tumble = init_tumble(25) ... while(some condition) { $tumble->(); ... do some stuff ... }

    I hope this is of use.

    Cheers,

    JohnGG

Re: "percent task completed progress bar" in cli interface, how?
by HuckinFappy (Pilgrim) on Jun 12, 2006 at 15:38 UTC
Re: "percent task completed progress bar" in cli interface, how?
by liverpole (Monsignor) on Jun 12, 2006 at 18:31 UTC
    Hi leocharre,

    A method I like to use for progress meters is to create a closure that lets you define all parameters up front, and then call the closure to update.

    Here's an example:

    #!/usr/bin/perl -w use strict; use warnings; sub progress_bar { my ($total, $msg, $width, $off_sym, $on_sym) = @_; $msg ||= '<count> / <total> (<percent> %) <meter>'; $width ||= 32; $off_sym ||= "-"; $on_sym ||= "*"; my $last_msg = ""; local $| = 1; my $psub = sub { my ($count) = @_; my $b_done = ($count < 0); $b_done and $count = $total; my $pcnt = sprintf "%5.1f", 100.0 * $count / $total; my $used = $pcnt * $width / 100; my $unused = $width - $used; my $meter = ($on_sym x $used) . ($off_sym x $unused); my $this_msg = $msg; $this_msg =~ s/<percent>/$pcnt/g; $this_msg =~ s/<count>/$count/g; $this_msg =~ s/<total>/$total/g; $this_msg =~ s/<meter>/$meter/g; my $next_width = $width - length($msg); if ($last_msg ne $this_msg) { print STDERR "\r$this_msg\e[K"; } $b_done and print STDERR "\n"; }; return $psub; } # Main program my $psub = progress_bar(10000, '<meter> File <count> of <total> (<perc +ent> %)'); for (my $i = 0; $i < 10000; $i++) { select(undef,undef,undef,0.01); $psub->($i); } $psub->(-1);
    In this program, the subroutine progress_bar creates the closure, based on the following 5 parameters (of which only the first is required):
    1. total ..... The total number of "things" to count (eg. maximum value)
    2. msg ....... The message to embed within the progress bar. (The default is a preformatted message). It can contain any of the following tags, which are dynamically substituted with the appropriate value:
      1. <count> ..... The current count
      2. <total> ..... The total count
      3. <percent> ... The current percentage complete
      4. <meter> ..... The progress meter itself
    3. width ..... The total width of the progress meter and message (default = 50)
    4. off_sym ... The "off" symbol (default = '-')
    5. on_sym .... The "on" symbol (default = '*')

    Then, after creating the closure, you call it with the current count, each time you wish to display the progress meter and message.  It will only update when the progress meter or message changes.

    When you're done, you can call it with a negative count to signal 100% completion (and to display a newline).  The main program shows an example of calling the progress meter with a file count that goes from zero to 1000.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/