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

Actually this is sort of related to a question I saw earlier about reporting the status of something being done. One thing I always sort of wanted to do is make a spinner. By spinner I mean a simple section of text which changes from | to / to - to \ . While I was booting Free BSD and watching one, I thought about how something like this could enhance my programs, and at least show that they're still working (and not hanging) - this became especially apperent today when I wrote an encryption program that takes a LONG time. If I could somehow show something happening around every 100th iteration of the main loop, I could at least show that it is doing something. But here's the catch, I want it to work in both Unix AND Windows. I know Term has the functionality I need, but it doesn't support Windows. About the only thing I could come up with was the following (this is just example code):

#!/usr/bin/perl my $spinner; my @spinState = ("-", "\\", "|", "/"); foreach(1..25) { print "Working: " . $spinState[$spinner] . "\n"; print " \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; sleep(1); }

And as you can see that's basically a pile of slop and not any real sort of solution. Actually just being able to overwrite the last character written to the screen is all I need. Any Ideas?

Replies are listed 'Best First'.
Re: making a spinner
by blakem (Monsignor) on Sep 20, 2001 at 22:40 UTC
    This seems to work on my unix VT100 terminal. It involves unbuffering the output, and printing the '\b' or backspace char.
    #!/usr/bin/perl -wT use strict; $| = 1; # unbuffer output my $spinner; my @spinState = ("-", "\\", "|", "/"); print "Working: "; foreach(1..25) { print "$spinState[$_%@spinState]"; sleep 1; print "\b"; # backspace }


Re: making a spinner
by shotgunefx (Parson) on Sep 20, 2001 at 22:29 UTC
Re: making a spinner
by phubuh (Beadle) on Sep 20, 2001 at 22:54 UTC
    "\b" is for backspace, so you could do this:
    $|++; for (my $x = 0; $x <= 25; ++$x){ foreach (("-", "\\", "|", "/")){ print $_; sleep 1; print "\b"; } }
Re: making a spinner
by archen (Pilgrim) on Sep 20, 2001 at 23:17 UTC
    yeah, \b seems to work (okay, I feel stupid now). I could have sworn there was something about it not working in an MSDOS prompt. Perhaps I confused myself recalling not being able to check input as it happens in DOS.


    Here's what the revised code looks like:

    ... print "Working: " . $spinState[$spinner]; while(read(INFILE, $buffer, 1024)) { print CRYPTFILE $cipher->crypt($buffer); &status() if(($wrap = ($wrap + 1) % 50) && not $silent); } print CRYPTFILE $cipher->finish; #-- flush remaining crap in buffe +r ... sub status() { $spinner = ($spinner + 1) % 4; print "\b$spinState[$spinner]"; }
      there's a variety of ascii/ escape codes that you can also use, if you're so inclined.
Re: making a spinner
by perlisfun (Sexton) on Sep 21, 2001 at 01:05 UTC

    Long time ago, i wrote something to transfer database table from Informix to Oracle, i used a spinner. It may help you.

    sub transfer_Data { my $stn = shift; my $dtn = shift; $| = 1; my @spinner = ('|','/','-','\\'); my $ifmx_stmt = "select count(*) from $stn" ; my $sth_ifmx = $db_ifmx->prepare($ifmx_stmt); $sth_ifmx->execute; my $count = $sth_ifmx->fetchrow(); print "Total record number: $count \n\n"; $ifmx_stmt = "select * from $stn" ; $sth_ifmx = $db_ifmx->prepare($ifmx_stmt); $sth_ifmx->execute; my $rn = 1; while ( my @row = $sth_ifmx->fetchrow_array ) { my $ds = ''; for ( my $i = 0; $i <@row; $i++ ) { my $nd = trim($row[$i]); $nd =~ s/'/''/g; my $check = match_DateList($i); if ( $check ) { $ds .= "to_date('$nd', 'MM/DD/YYYY'),"; } else { $ds .= "'$nd',"; } } $ds =~ s/,$/)/; $ds =~ s/^/(/; my $orac_stmt = "insert into $dtn values " . $ds ; my $sth_orac = $db_orac->do($orac_stmt); print "Record: $rn $spinner[$rn%4]\r"; $rn++; } $sth_ifmx->finish; print "\nData Transfer is complete \n\n"; $| = 0; }
    It may be too much. That is what i have.

    Edit by tye

Re: making a spinner
by demerphq (Chancellor) on Sep 22, 2001 at 20:44 UTC
    Cool! Two uses for Tie::Cycle* in one day!
    use Tie::Cycle; my $delay=5; tie my $spinner, 'Tie::Cycle', [map {("\b$_")x$delay}qw(\ | / -)]; print $spinner while (1);

    You are not ready to use symrefs unless you already know why they are bad. -- tadmc (CLPM)
    * and no I didn't write it... :-)

Re: making a spinner
by grummerX (Pilgrim) on Sep 21, 2001 at 17:58 UTC
    The above suggestions utilizing "\b" should work fine for your purposes, but if you need to do any terminal manipulation in Windows, you should look into Win32::Console.

    The following code basically duplicates the "\b" examples utilizing Win32::Console:

    #!c:/perl/bin/perl -w use strict; use Win32::Console; my $console = new Win32::Console(STD_OUTPUT_HANDLE); $console->Cursor(-1,-1,-1,0); # Turn off cursor blink foreach (1..3) { foreach( ("-", "\\", "|", "/") ){ $console->Cursor(0); #Move cursor to beginning of line $console->Write($_); sleep 1; } } $console->Cursor(-1,-1,-1,1); # Reset cursor blink
    Using "\b" is obviously preferable in this case, but if you're looking into doing anything more complex Win32::Console is a good thing to keep in mind.


Re: making a spinner
by snafu (Chaplain) on Sep 21, 2001 at 17:45 UTC
    Between this and the other set of posts on this same topic, I think it would be a good idea to make a module for the whirleywig. I believe in the node A whirleygig for a progress indicator for scripts someone mentioned a module for this. I don't remember very well. If that wasn't a real .pm will someone make one? ;) I would but I don't have the time right now nor the knowledge of module making. BUT! It would be a great exercise for me.

    - Jim