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

Hi Monks,

I've RTFM'd and STFW'd but found no enlightenment.

I have successfully been using Term::ProgressBar to monitor my prog's processing of a file. I now want to extend the program to process multiple files. Aha, I thought to myself, it would be nice to have two ProgressBars, one above the other: one to monitor progress through the file set; and one to monitor progress in the file currently being processed. However, this doesn't seem possible. Only one bar is displayed at a time, the last one to be created.

My program is too long long to post here, but here is an example adapted from one of the examples in the Term::ProgressBar package:

#!/usr/bin/perl use warnings; use strict; use Term::ProgressBar 2.00; use constant MAX => 100_000; my $run_progress = Term::ProgressBar->new({name => 'Run', count => 1 +0, remove => 1}); $run_progress->minor(0); my $run_update = 0; for (0..10) # the outer run { my $powers_progress = Term::ProgressBar->new({name => "Powers$_", co +unt => MAX, remove => 1}); $powers_progress->minor(0); my $powers_update = 0; for (0..MAX) { my $is_power = 0; for(my $i = 0; 2**$i <= $_; $i++) { $is_power = 1 if 2**$i == $_; } $powers_update = $powers_progress->update($_) if $_ >= $powers_update; } $powers_progress->update(MAX) if MAX >= $powers_update; $run_update = $run_progress->update($_) if $_ >= $run_update; #print "\n"; }

Running this shows only the powers_progress bars, and not the run_progress bars. Commenting in the print statement on the 2nd to last line displays the run_progress bar as a "snap shot".

Is what I am trying to do even possible? I'm truly stuck on this problem.

Thanks in advance for any help,

poshboy

p.s. this is my first post to the Monks, so please excuse any mistakes I've made!

Replies are listed 'Best First'.
Re: Simultaneous Term::ProgressBars?
by jethro (Monsignor) on Aug 07, 2008 at 11:19 UTC

    Term::ProgressBar updates the progress bar simply by printing a carriage return character before the bar (as far as I can tell from the source). That character just sets the cursor at the beginning of the line again

    You can test this with a simple print "\n"; inbetween updates of the bar. Then you should see every progress bar iteration one line after the other, because the new progress bar is printed on the next line instead of the same.

    You might be able to use the minor character (see ProgressBar docu) to display progress inside the files if you use it with the linear completion time estimator to just stay inside one step of the major character while using the minor character to go from 0 to 100% for any number of files.

    CORRECTION: Sorry, didn't mean linear completion time estimator but the return value of update. If that shows the change value of the major progress bar, then it is possible. If it shows the change value of the minor progress bar too, you are out of luck (and I think the latter case is more likely).

      Printing a "\n" almost works. Adding a print "\e[A" to move up one line gets you the whole way.
Re: Simultaneous Term::ProgressBars?
by zentara (Cardinal) on Aug 07, 2008 at 11:24 UTC
    Without trying to run your code, I think to do multiple files at once, you should move up to an event-loop system like Curses or POE or Glib or preferably a GUI that can have multiple windows. I'm guessing you are running into a limitation of Term::Progressbar, which probably assumes a single terminal window. You can try to write your own. The following is a single pbar, but you could add another on the same line. Having them one on top of another would get very tricky. I know you can backspace on a line, but I've never seem jumping up 1 line on a terminal without curses.
    #!/usr/bin/perl use warnings; $|=1; for my $x(1..100){ print progress_bar( $x,100,25,'='); sleep 1; } 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; }
    If you are willing to switch to a gui
    #!/usr/bin/perl use warnings; use strict; use Tk; use Tk::ProgressBar; my $mw = MainWindow->new(); my $pf = $mw->Frame()->pack(); my $pf1 = $mw->Frame()->pack(); my $foo = 0; my $p = $pf->ProgressBar( -troughcolor => 'black', -fg => 'lightgreen', -blocks => 1, -width => 20, -length => 200, -from => 0, -to => 100, -variable => \$foo, )->pack(-side =>'right'); my $l1 = $pf->Label(-text => '%', -bg => 'black', -fg => 'green', )->pack(-side => 'right'); my $l2 = $pf->Label(-textvariable => \$foo, -bg => 'black', -fg => 'green', -width => 3, )->pack(-side => 'right'); ############################### my $foo1 = 0; my $p1 = $pf1->ProgressBar( -troughcolor => 'black', -fg => 'pink', -blocks => 1, -width => 20, -length => 200, -from => 0, -to => 100, -variable => \$foo1, )->pack(-side =>'right'); my $l3 = $pf1->Label(-text => '%', -bg => 'black', -fg => 'pink', )->pack(-side => 'right'); my $l4 = $pf1->Label(-textvariable => \$foo1, -bg => 'black', -fg => 'pink', -width => 3, )->pack(-side => 'right'); $mw->Button( -text => 'Quit', -command => sub { exit } )->pack; $mw->repeat( 100, sub { $foo = ( $foo + 1 ) % 100; if($foo > 100){$foo = 0} $foo1 = ( $foo1 + 2 ) % 100; if($foo1 > 100){$foo1 = 0} }); MainLoop;

    I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: Simultaneous Term::ProgressBars?
by Tanktalus (Canon) on Aug 07, 2008 at 22:09 UTC

    Here's a working version. Note that I'm using Term::StatusBar, not Term::ProgressBar. Then again, zentara didn't use T::PB, either :-)

    #! /usr/bin/perl use warnings; use strict; use Term::StatusBar; system('clear'); # too lazy to do this right. use constant MAX => 100_000; use Time::HiRes qw(usleep); my $run_progress = Term::StatusBar->new(label => 'Run', totalItems => 10, scale => 1000, ); for (0..10) # the outer run { $run_progress->update(); my $powers_progress = Term::StatusBar->new(label => "Powers$_", startRow => 3, scale => 1000, totalItems => MAX, ); for (0..MAX) { $powers_progress->update(); usleep(100); } }
    It's pretty peppy, too, on my machine. Might go slower on a slower machine. <snicker>