tags:

views:

779

answers:

4

What would be an easy way of implementing a console-based progress indicator for a task that's being executed, but I can't anticipate how much time it would take?

I used to do this back when I coded in Clipper, and it was only a matter of iterating through the chars '/', '-', '\', '|' and positioning them in the same place.

Any way / links / libs for doing that (or something similar) in C++?

The target for this is *nix environments.

Edits:

  • changed the title to be more coherent and generic;
  • added target environment.
+8  A: 

A very simple way to do it is to print out a string followed by a '\r' character. That is carriage return by itself and on most consoles, it returns the cursor to the beginning of the line without moving down. That allows you to overwrite the current line.

If you are writing to stdout or cout or clog remember to fflush or std::flush the stream to make it output the line immediately. If you are writing to stderr or cerr then the stream is unbuffered and all output is immediate (and inefficient).

A more complicated way to do it is to get into using a screen drawing library like curses. The Windows consoles have some other ways of setting them for direct screen writing but I don't know what they are.

Zan Lynx
Thanks Zan, the '\r' tip sounds worth trying!
kolrie
Conio seems like the one for Windows.. http://en.wikipedia.org/wiki/Conio.h
perimosocordiae
As Zan suggested, I would use curses. A quick search for "curses tutorial" will get you just what you need...
Tal Pressman
The \r solution worked and is exactly what I needed - simplest case that works.
kolrie
If one goes for a Windows-specific solution, there's no point in bothering with conio.h (since it's platform-specific anyway) - just use Win32 API console functions.
Pavel Minaev
+5  A: 

You could try something like:

void
spinner(int spin_seconds) {
    static char const spin_chars[] = "/-\\|";
    unsigned long i, num_iterations = (spin_seconds * 10);
    for (i=0; i<num_iterations; ++i) {
        putchar(spin_chars[i % sizeof(spin_chars)]);
        fflush(stdout);
        usleep(100000);
        putchar('\b');
    }
}

Of course, this is non-standard because of the sub-second usleep() and I'm not sure if there is any guarantee that \b erases a character or not, but it works on most platforms. You can also try \r instead if \b doesn't do the trick. Otherwise, try to find a version of curses.

Edit (curses sample)

This should get you started:

#include <curses.h>
#include <unistd.h>

void spinner(int spin_seconds) {
    static char const spin_chars[] = "/-\\|";
    unsigned long i, num_iterations = (spin_seconds * 10);
    for (i=0; i<num_iterations; ++i) {
        mvaddch(0, 0, spin_chars[i & 3]);
        refresh();
        usleep(100000);
    }
}

int main() {
    initscr();    /* initializes curses */
    spinner(10);  /* spin for 10 seconds */
    endwin();     /* cleanup curses */
    return 0;
}

Make sure to link with either -lcurses or -lncurses. That should work on any UNIX alike out there.

D.Shawley
Amazing, thank so much for this. I will do some experimentation with this with learning purposes, because the \r solution was enough for me. That's the only reason that kept me from choosing your - thoroughly explained - answer. Thank you!
kolrie
+1  A: 

Wow, clipper, perhaps you are talking about the @row,col things built in to the language? (Rhetorical question only...)

You can do simple progress bars with printf: you can leave out the trailing newline. You can obviously begin or end the string with \b in order to overprint characters. It's easy to do the traditional -\|/ kind that way.

I recall that the Eclipse UI guidelines recommended progress indicators regardless of how much you were able to tell about the actual progress. I think theory was that anything is better than nothing and to just do the best you can.

The only trick that you are likely to need is potentially to defeat line buffering. Be sure to fflush(stdout) after each output operation. (Or ostream::flush())

DigitalRoss
If you don't mind me replying to your rethorical question, yes we used to do something like that. Good old Clipper days :-)
kolrie
I was wondering why mine works without flush, then I realized I am using stderr which is unbuffered.
Zan Lynx
+1  A: 

Boost has a progress library which can help some of these things

JRB