Progress indicator in terminal-based applications

In terminal-based applications, there are various ways one can show progress indicators:

Simple series of dots:

Copying files .
Copying files ..
Copying files ...
Copying files .....
Copying files ......

Spinning cursors:

Copying files |
Copying files /
Copying files -
Copying files \
Copying files |

Or progress bars (of various styles):

Copying files [=         ] 10%
Copying files [==        ] 20%
Copying files [===       ] 30%
Copying files [====      ] 40%
Copying files [=====     ] 50%

With Progress::Any, you can update progress indicators and then show them in various forms just by swithing output (Progress::Any::Output::*) modules, without re-coding your code. For terminal-based applications, so far these are the (useful) output modules available for terminal applications: TermProgressBarColor (for progress bars), TermSpin (for spinning cursors). Here’s a very minimal code to show progress:

use Progress::Any '$progress';
use Progress::Any::Output 'TermProgressBarColor', width=>40;
$|++;
print "Copying files:";
for (1..10) {
    $progress->update;
    sleep 1;
}
$progress->finish;
print "done\n";

Sample output:

Copying files:   0% [ ==              ]1s left         
Copying files:   0% [  ==             ]1s left         
Copying files:   0% [   ==            ]1s left         
Copying files:   0% [    ==           ]1s left         
Copying files:   0% [     ==          ]1s left         
...
Copying files: done

Since we have not provided any target, completion percentage and ETA (estimated time of “arrival”, or completion) cannot be calculated. To specify target, you can add:

$progress->target(10);

before the loop. Output:

Copying files:  10% [=                ]1s left         
Copying files:  20% [===              ]4s left         
...
Copying files:  90% [================ ]1s left         
Copying files: done

To use another output, just change this output line, e.g.:

use Progress::Any::Output 'TermSpin';
Copying files: |
Copying files: /
Copying files: -
...
Copying files: done

The spinning cursor output does not show completion percentage or ETA.

There are many other features available, e.g. TermProgressBarColor can show text message inside the bar, can autoset width to the whole terminal width. You can have separate progress indicators for multiple (sub)tasks. And there are other outputs for non-terminal applications, e.g. for GUI or for logging. Please refer to the documentation for all these.

One last thing I want to note here is that the terminal progress output modules have been written to work together with Log::Any, particularly with the ScreenColoredLevel adapter. The log adapter and progress output modules all write to terminal, and care has been taken so they do not clobber each other’s outputs. Here’s a full example:

#!/usr/bin/env perl

use 5.010;
use strict;
use warnings;

use Log::Any '$log';
use Log::Any::Adapter 'ScreenColoredLevel', min_level=>'info';

use Progress::Any '$progress';
use Progress::Any::Output ('TermProgressBarColor', show_delay=>2);

use Time::HiRes qw(sleep time);

$progress->target(3);

$log->info('Starting');

$log->info('Doing task 1/3 ...');
{
    my $target1 = 30+int(40*rand());
    for (1..$target1) {
        $progress->update(pos => $_/$target1);
        if (rand() < 0.025) {
            $log->warn("There's something wrong with item $_");
        }
        sleep 0.1;
    }
}

$log->info('Doing task 2/3 ...');
{
    my $target2 = 30+int(40*rand());
    for (1..$target2) {
        $progress->update(pos => 1 + $_/$target2);
        sleep 0.1;
    }
}

$log->info('Doing task 3/3 ...');
{
    my $target3 = 30+int(40*rand());
    for (1..$target3) {
        $progress->update(pos => 2 + $_/$target3);
        sleep 0.1;
    }
}

$log->info('Finished');

One nice thing about the example above is the usage of TermProgressBarColor’s show_delay parameter (TermSpin also has the same parameter, BTW). By setting this parameter, the progress output will not be shown first until the specified number of seconds has passed (the counter is reset to zero whenever a new log message is displayed to the screen). This has the effect of less chatty output (only log messages are shown, without progress report), but if several seconds pass without any more message being displayed to the screen, the progress output automatically shows.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s