I always find 'silent' programmes especially irritating. There's nothing worse than starting a process and then watching a frozen screen in the hope of seeing some sign of progress. This is especially irritating with CLI programmes - you just don't know if it's busy, crashed or plain confused.
I've been working on a directory synchroniser recently and this very issue, of letting the user know that something is happening, came up. There are all sorts of ways to solve it. Simple but inelegant solutions include filling the screen with lines of detail showing what's going on, printing a single dot for each pass through the programme's loop, even writing a copious log and sneeringly advising the user to tail -f the file. Then there are the pretty, but complicated solutions, all of which tend to be a variation on the progress bar theme. Beside taking a lot of effort, these tend to impose a significant run-time overhead and, for a simple little job such as my synchroniser project, seem like massive overkill.
There is, however, one very neat and simple CLI solution: use a spinner. Although GUI spinners can be complicated, in text mode Perl, they are almost laughably trivial, although I don't recall seeing this approach described anywhere. The slight spin (ouch!) on my approach, is that I needed the progress to be shown in a sub-routine that calls itself recursively, but that's quite easy with some global variables.
I only needed three variables to implement the spinner. I defined them with the vars package, as the routines that will use them are in packages outside the main script...
use vars qw/
@Spinner
$ProgressCount
$BackSpace
/;
@Spinner is a simple array for the characters that make up the spinning wheel, $ProgressCount is an integer that decides which character to display and $BackSpace is, as its name suggests, the delete backwards character.
Then, still in the main script, I only had to initialise them...
@Spinner = ('!', '/', '-', '\\', '!', '/', '-', '\\');
$ProgressCount = 0;
$BackSpace = "\010";
The spinner characters are pretty obvious, except that you need to remember to escape the backslash, to avoid any parsing problems. The progress counter is set to 0 when we begin, although it could be any valid index into the spinner array. The backspace value should work equally well for Windows and Unix systems, although I've only tested it on Windows XP and Apple OS-X so far.
Finally, to use the spinner, you only need three lines, at the point where you're showing the progress...
print $::BackSpace . $::Spinner[$::ProgressCount];
if( $::ProgressCount++ > 6 ) { $::ProgressCount = 0; }
$=1;
The first line backspaces over whatever was there, then shows the currently selected character. The second line increments the progress counter and checks it hasn't got too big. If it has, it resets it. The third line forces Perl to flush the print buffer, thus making sure that we see the progress as it occurs. If you don't do this, you could end up with nothing apparently happening, which rather negates the point of this excercise.
By the way, the syntax '$::varname' just points 'varname' at the calling $main, in this case, our control script.
:
Subscribe to:
Post Comments (Atom)
Followers
Who is this Sejanus character anyway?
- Sejanus
- I'm a British freelance Analyst Programmer who has spent the last 25 years working on everything from microcontrollers to mainframes. I use a wide variety of languages at work but try to stick to C and Perl for my own projects.
No comments:
Post a Comment