How do I add a persistent progress bar to a C ++ program that produces terminal output (on Linux)?

I have an existing program that contains a loop over files. It performs various functions, providing a lot of terminal output. I want to have a generic progress bar that stays stationary on the same line at the bottom of the terminal while all output from the activity files is printed above it. How should I try to do something like this?


EDIT: So, to be clear, I'm trying to solve display problems inherent in something 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);
  }
}

      

+2


source to share


1 answer


The only portable way of moving the cursor that I know of is using \r

to go to the beginning of the line. You say that you would like to bring material above progress. Luckily, you're in luck since you're on Linux, and you can use terminal exit codes to navigate the terminal freely. Take a 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 using the escape code \033[F

before printing. Then print one line, moved one line with \n

and reprint the progress.

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

before exiting.



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 achieve this behavior without using additional libs like ncurses

, but perhaps this 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;
}

      

+2


source







All Articles