How should I add a stationary progress bar to a C++ program that produces terminal output (in Linux)?

3.5k Views Asked by At

I have an existing program that contains a loop over files. It does various things, providing lots of terminal output. I want to have an overall progress bar that remains stationary on the same line at the bottom of the terminal while all of the output from the file operations is printed above it. How should I try to do something like this?


EDIT: So, just to be clear, I'm trying to address the display problems inherent in something a bit like the following:

#include <unistd.h>
#include <iostream>
#include <string>

using namespace std;

int main(){
  for (int i = 0; i <= 100; ++i){
    std::cout << "processing file number " << i << "\n";
    string progress = "[" + string(i, '*') + string(100-i, ' ') + "]";
    cout << "\r" << progress << flush;
    usleep(10000);
  }
}
1

There are 1 best solutions below

2
On BEST ANSWER

The only portable way of moving the cursor around that I know of is using \r to move to the beginning of the line. You mention that you would like to output stuff above the progress. Fortunately, you are in luck since you're on Linux and you can use the terminal escape codes to move around the terminal freely. Look at this example:

#include <unistd.h>
#include <iostream>
#include <string>

using namespace std;

int main()
{
  cout << endl;
  for (int i=0; i <= 100; ++i)
  {
    string progress = "[" + string(i, '*') + string(100-i, ' ') + "]";
    cout << "\r\033[F" << i << "\n" << progress << flush;
    usleep(10000);
  }
}

Here, I added the ability to print the progress value above the progress bar by moving to the beginning of the line using \r and one line up using escape code \033[F before printing. Then, printed one line, moved down one line with \n and re-printed the progress.

You can go even further and move your cursor to any X,Y position in the terminal before printing. For that use the escape code \033[Y;Xf before your output.

For a good list of escape codes, check out Wikipedia: https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes

So, it is possible to achive that behavior without using additional libs like ncurses, but maybe it is actually what you want if you intend to create a more gui-like experience.

Fixing your attempt:

void print_progress_bar(int percentage){
  string progress = "[" + string(percentage, '*') + string(100 - percentage, ' ') + "]";
  cout << progress << "\r\033[F\033[F\033[F" << flush;
}

int main(){
  cout << endl;
  for (int i=0; i <= 100; ++i){
    std::cout << "processing file number " << i << "\n";
    std::cout << "    doing thing to file number " << i << "\n";
    std::cout << "    doing another thing to file number " << i << "\n";
    print_progress_bar(i);
    usleep(10000);
  }
  cout << endl;
  cout << endl;
  cout << endl;
  cout << endl;
}