Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

printing in a specific area of the console

by io (Scribe)
on Feb 17, 2002 at 19:28 UTC ( #146025=perlquestion: print w/ replies, xml ) Need Help??
io has asked for the wisdom of the Perl Monks concerning the following question:

dear monks, This is either going to be very easy or very hard (I think). Let me try to explain: I am writing a little script that fetches pages from the www like many of us problebly have done. I'd like to include a little progress indicator, which is not very hard when you can read the 'content-length' http header. My code looks like this:
while ($total < $length) { $read = read($remote, $buffer, int($length/100)); $stuff .= $buffer; $total += $read; $done = sprintf("%.2f", $total/($length/100)); print "$done%\n"; }
This works just fine. It's only that every loop the progress is printed on a new line (in the console). Much nicer would be if it would stay on the same place all the time. I hope you know what I mean. I think I saw the CPAN shell do this. Many console applications do this too, like rpm. Thanks very much.

Comment on printing in a specific area of the console
Download Code
Re: printing in a specific area of the console
by stephen (Priest) on Feb 17, 2002 at 19:55 UTC
    So long as you're printing on an ANSI-compatible terminal, you can control the cursor by means of escape sequences. The easiest way to do this would be the Term::ANSIScreen module from CPAN. You can see an example here.


Re: printing in a specific area of the console
by belg4mit (Prior) on Feb 17, 2002 at 19:57 UTC
    TIMTOWTDI, the easiest is to print a control-M (\r) before you print your update. HOwever, you shoul dbe aware that perl buufers it's ouput and it'll bite you with this idf you're not careful. You should disable buffering by doing $|++.

    UPDATE</b: And of course dropping the newline as others (inc. Speedy) have pointed out.

    perl -pe "s/\b;([st])/'\1/mg"

Re: printing in a specific area of the console
by Speedy (Monk) on Feb 17, 2002 at 20:01 UTC
    I suspect you need to drop the \n at the end of the print statement (or each print is guaranteed to be on the next line), and on each loop iteration print only the "difference" between the value of the last $done ($done_last)? and the current $done (scaled appropriately). This will append the "new stuff" to what you have already printed.

    self is an illusion
Re: printing in a specific area of the console
by Masem (Monsignor) on Feb 17, 2002 at 20:07 UTC
    There's several tricks you can use, either using a module like Curses that takes advantage of positioning on a term, or by using characters like \r or \b which, on most unixen terms, will back up the cursor position. You might want to look at this node that asks a similar question for a full list of ideas.

    Note that if you're just putting a line of symbols on the screen, without extra decoration, you can always print those out sequentially when they are needed (eg, one or two characters at a time), without an extra modules.

    Dr. Michael K. Neylon - || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

Re: printing in a specific area of the console
by steves (Curate) on Feb 17, 2002 at 20:14 UTC

    The simple way is to not print a newline after each piece. Then, before each new piece, print as many backspaces as you need ("\b") to get you back to the start of where the new data will go. The new data will have to be at least as long as the old, so pad anything shorter with spaces. Do this with standard output unbuffered. Here's a simple sub I wrote to print ongoing totals:

    use strict; use FileHandle; use vars qw(@ISA @EXPORT $VERSION); use Exporter; $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = qw(print_total); sub print_total($$) { my $total = shift; my $label = shift; my $n; if ($total == 1) { STDOUT->autoflush; print $label; } $n = length($total); print "$total"; while ($n-- > 0) { print "\b"; } }

    The other fun one is to print sequences of | / - \ to get one of those spinning progress indicators.

Re: printing in a specific area of the console
by Amoe (Friar) on Feb 17, 2002 at 21:07 UTC

    A nice way to do this is the way provided by the LWP::UserAgent module. It allows you to provide a code reference that gets invoked whenever it gets another chunk of a file. Check this:

    my $sub = sub { my ($data, $res, $proto) = @_; # either write directly to file or push onto an array $filename ? print FILE $data : push(@res_lines, $data) +; # increment total length $data_recved += length $data; if ($already_drew_percentage) { # erase an old sign and draw a new one print "\b" x 9, sprintf('%03d%% done', (($data_recved / $res->content_length) * 100) +); } else { # draw the first percentage printf('%03d%% done', (($data_recved / $res->content_length) * 100) +); $already_drew_percentage = 1; } };

    (Sorry about the tab mess...I'm not sure how to correct it.) Call it like this:

    my $res = $agent->request($req, $sub, 1024)

    Where $agent is an LWP::UserAgent, $req is an HTTP::Request and 1024 is the amount of bytes you want to get before invoking $sub.

    That was ripped from the pronbot source. It allows you to either write binaries directly to file via the FILE handle and the value of $filename, or push the lines of the response onto an array which can later be joined. (You might have do declare some of the vars yourself :) I'd advocate using an LWP module for basic HTTP stuff, it's very easy, although this probably looks complex.

    my one true love
Re: printing in a specific area of the console
by gellyfish (Monsignor) on Feb 17, 2002 at 21:55 UTC

    The CPAN shell does this with Term::ReadKey which I am currently looking after ;-}


Re: printing in a specific area of the console
by io (Scribe) on Feb 18, 2002 at 11:33 UTC
    thanks very much for your replies, I think print "\b\b\b" will work for my quick hack

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://146025]
Approved by root
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (12)
As of 2014-07-30 16:11 GMT
Find Nodes?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:

    Results (235 votes), past polls