Reputation: 42255
I'm writing a console program in C++ to download a large file. I know the file size, and I start a work thread to download it. I want to show a progress indicator to make it look cooler.
How can I display different strings at different times, but at the same position, in cout or printf?
Upvotes: 103
Views: 146335
Reputation: 740
Simple, you can just use string's fill constructor:
#include <iostream> //for `cout` and flush
#include <string> //for the constructor
#include <iomanip> //for `setprecision`
using namespace std;
int main()
{
const int cTotalLength = 10;
float lProgress = 0.3;
cout <<
"\r[" << //'\r' aka carriage return should move printer's cursor back at the beginning of the current line
string(cTotalLength * lProgress, 'X') << //printing filled part
string(cTotalLength * (1 - lProgress), '-') << //printing empty part
"] " <<
setprecision(3) << 100 * lProgress << "%" << //printing percentage
flush;
return 0;
}
Which would print:
[XXX-------] 30%
If you need it in pure C
and you would like to be able to customize the size and filler characters at runtime:
#include <stdio.h> //for `printf` and `fflush`
#include <stdlib.h> //for `malloc`
#include <string.h> //for `memset`
int main()
{
const int cTotalLength = 10;
char* lBuffer = malloc((cTotalLength + 1) * sizeof *lBuffer); //array to fit 10 chars + '\0'
lBuffer[cTotalLength] = '\0'; //terminating it
float lProgress = 0.3;
int lFilledLength = lProgress * cTotalLength;
memset(lBuffer, 'X', lFilledLength); //filling filled part
memset(lBuffer + lFilledLength, '-', cTotalLength - lFilledLength); //filling empty part
printf("\r[%s] %.1f%%", lBuffer, lProgress * 100); //same princip as with the CPP method
//or you can combine it to a single line if you want to flex ;)
//printf("\r[%s] %.1f%%", (char*)memset(memset(lBuffer, 'X', lFullLength) + lFullLength, '-', cTotalLength - lFullLength) - lFullLength, lProgress * 100);
fflush(stdout); //streams are usually buffered and not get printed until you send `\n` or "flush" it
free(lBuffer);
return 0;
}
but if you don't need to customize it at runtime:
#include <stdio.h> //for `printf` and `fflush`
#include <stddef.h> //for `size_t`
int main()
{
const char cFilled[] = "XXXXXXXXXX";
const char cEmpty[] = "----------";
float lProgress = 0.3;
size_t lFilledStart = (sizeof cFilled - 1) * (1 - lProgress);
size_t lEmptyStart = (sizeof cFilled - 1) * lProgress;
printf("\r[%s%s] %.1f%%",
cFilled + lFilledStart, //Array of Xs starting at `cTotalLength * (1 - lProgress)` (`cTotalLength * lProgress` characters remaining to EOS)
cEmpty + lEmptyStart, //Array of -s starting at `cTotalLength * lProgress`...
lProgress * 100 //Percentage
);
fflush(stdout);
return 0;
}
Upvotes: 1
Reputation: 1
I provide an internative method using std::setfill() and std::setw().
#include <iostream>
#include <iomanip>
#include <chrono>
#include <thread>
static void printProgressBar(int percent, int progressWidth=60)
{
std::cout << "\r[" << std::setfill('#') << std::setw(percent*progressWidth/100) << '#';
std::cout << std::setfill(' ') << std::setw(progressWidth*(100-percent)/100) << "]";
std::cout << std::setw(3) << percent << "%";
std::cout.flush();
}
int main()
{
using namespace std::chrono_literals;
for (int i=0; i<=100; i+=1)
{
printProgressBar(i, 100);
std::this_thread::sleep_for(20ms);
}
return 0;
}
Upvotes: 0
Reputation: 39
My very simple C solution:
#include <stdio.h>
#define S_(x) #x
#define S(x) S_(x)
#define PBWIDTH 64
#define PBCHAR '#'
static void progressbar(unsigned percent) {
char pbstr[PBWIDTH];
memset(pbstr, PBCHAR, PBWIDTH);
fprintf(stderr, "\r[%-" S(PBWIDTH) ".*s] %u%%",
percent * PBWIDTH / 100, pbstr, percent);
}
int main(void) {
progressbar(70);
fprintf(stderr, "\n");
}
Outputs:
[############################################ ] 70%
The memset
should be optimized to 1 or 2 vector instructions when compiled for modern CPUs, much smaller than storing the whole string statically and equally fast.
In the programs I use it in, I like to print 0% this way, before the loop that calls progressbar
:
fprintf(stderr, "[%-" S(PBWIDTH) ".*s] %u%%", 0, "", 0);
If you prefer the number to be before the bar, change the fprintf
in progressbar
:
fprintf(stderr, "\r%3u%% [%-" S(PBWIDTH) ".*s]",
percent, percent * PBWIDTH / 100, pbstr);
And if you do the optional 0% bit:
fprintf(stderr, "%3u%% [%-" S(PBWIDTH) ".*s]", 0, 0, "");
Modify this all as you see fit. :)
Upvotes: 1
Reputation: 121
I needed to create a progress bar and some of the answers here would cause the bar to blink or display the percentage short of 100% when done. Here is a version that has no loop other than one that simulates cpu work, it only prints when the next progress unit is incremented.
#include <iostream>
#include <iomanip> // for setw, setprecision, setfill
#include <chrono>
#include <thread> // simulate work on cpu
int main()
{
int batch_size = 4000;
int num_bars = 50;
int batch_per_bar = batch_size / num_bars;
int progress = 0;
for (int i = 0; i < batch_size; i++) {
if (i % batch_per_bar == 0) {
std::cout << std::setprecision(3) <<
// fill bar with = up to current progress
'[' << std::setfill('=') << std::setw(progress) << '>'
// fill the rest of the bar with spaces
<< std::setfill(' ') << std::setw(num_bars - progress + 1)
// display bar percentage, \r brings it back to the beginning
<< ']' << std::setw(3) << ((i + 1) * 100 / batch_size) << '%'
<< "\r";
progress++;
}
// simulate work
std::this_thread::sleep_for(std::chrono::nanoseconds(1000000));
}
}
Upvotes: 1
Reputation: 79
Here is a simple one I made:
#include <iostream>
#include <thread>
#include <chrono>
#include <Windows.h>
using namespace std;
int main() {
// Changing text color (GetStdHandle(-11), colorcode)
SetConsoleTextAttribute(GetStdHandle(-11), 14);
int barl = 20;
cout << "[";
for (int i = 0; i < barl; i++) {
this_thread::sleep_for(chrono::milliseconds(100));
cout << ":";
}
cout << "]";
// Reset color
SetConsoleTextAttribute(GetStdHandle(-11), 7);
}
Upvotes: 3
Reputation: 189
May be this code will helps you -
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <cmath>
using namespace std;
void show_progress_bar(int time, const std::string &message, char symbol)
{
std::string progress_bar;
const double progress_level = 1.42;
std::cout << message << "\n\n";
for (double percentage = 0; percentage <= 100; percentage += progress_level)
{
progress_bar.insert(0, 1, symbol);
std::cout << "\r [" << std::ceil(percentage) << '%' << "] " << progress_bar;
std::this_thread::sleep_for(std::chrono::milliseconds(time));
}
std::cout << "\n\n";
}
int main()
{
show_progress_bar(100, "progress" , '#');
}
Upvotes: 0
Reputation: 10110
For a C
solution with an adjustable progress bar width, you can use the following:
#define PBSTR "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
#define PBWIDTH 60
void printProgress(double percentage) {
int val = (int) (percentage * 100);
int lpad = (int) (percentage * PBWIDTH);
int rpad = PBWIDTH - lpad;
printf("\r%3d%% [%.*s%*s]", val, lpad, PBSTR, rpad, "");
fflush(stdout);
}
It will output something like this:
75% [|||||||||||||||||||||||||||||||||||||||||| ]
Upvotes: 71
Reputation: 780
Another way could be showing the "Dots" or any character you want .The below code will print progress indicator [sort of loading...]as dots every after 1 sec.
PS : I am using sleep here. Think twice if performance is concern.
#include<iostream>
using namespace std;
int main()
{
int count = 0;
cout << "Will load in 10 Sec " << endl << "Loading ";
for(count;count < 10; ++count){
cout << ". " ;
fflush(stdout);
sleep(1);
}
cout << endl << "Done" <<endl;
return 0;
}
Upvotes: 5
Reputation: 601
I know I am a bit late in answering this question, but I made a simple class that does exactly what you want. (keep in mind that I wrote using namespace std;
before this.):
class pBar {
public:
void update(double newProgress) {
currentProgress += newProgress;
amountOfFiller = (int)((currentProgress / neededProgress)*(double)pBarLength);
}
void print() {
currUpdateVal %= pBarUpdater.length();
cout << "\r" //Bring cursor to start of line
<< firstPartOfpBar; //Print out first part of pBar
for (int a = 0; a < amountOfFiller; a++) { //Print out current progress
cout << pBarFiller;
}
cout << pBarUpdater[currUpdateVal];
for (int b = 0; b < pBarLength - amountOfFiller; b++) { //Print out spaces
cout << " ";
}
cout << lastPartOfpBar //Print out last part of progress bar
<< " (" << (int)(100*(currentProgress/neededProgress)) << "%)" //This just prints out the percent
<< flush;
currUpdateVal += 1;
}
std::string firstPartOfpBar = "[", //Change these at will (that is why I made them public)
lastPartOfpBar = "]",
pBarFiller = "|",
pBarUpdater = "/-\\|";
private:
int amountOfFiller,
pBarLength = 50, //I would recommend NOT changing this
currUpdateVal = 0; //Do not change
double currentProgress = 0, //Do not change
neededProgress = 100; //I would recommend NOT changing this
};
An example on how to use:
int main() {
//Setup:
pBar bar;
//Main loop:
for (int i = 0; i < 100; i++) { //This can be any loop, but I just made this as an example
//Update pBar:
bar.update(1); //How much new progress was added (only needed when new progress was added)
//Print pBar:
bar.print(); //This should be called more frequently than it is in this demo (you'll have to see what looks best for your program)
sleep(1);
}
cout << endl;
return 0;
}
Note: I made all of the classes' strings public so the bar's appearance can be easily changed.
Upvotes: 7
Reputation: 103505
You can use a "carriage return" (\r) without a line-feed (\n), and hope your console does the right thing.
Upvotes: 68
Reputation: 2436
Take a look at boost progress_display
http://www.boost.org/doc/libs/1_52_0/libs/timer/doc/original_timer.html#Class%20progress_display
I think it may do what you need and I believe it is a header only library so nothing to link
Upvotes: 14
Reputation: 45675
With a fixed width of your output, use something like the following:
float progress = 0.0;
while (progress < 1.0) {
int barWidth = 70;
std::cout << "[";
int pos = barWidth * progress;
for (int i = 0; i < barWidth; ++i) {
if (i < pos) std::cout << "=";
else if (i == pos) std::cout << ">";
else std::cout << " ";
}
std::cout << "] " << int(progress * 100.0) << " %\r";
std::cout.flush();
progress += 0.16; // for demonstration only
}
std::cout << std::endl;
[> ] 0 %
[===========> ] 15 %
[======================> ] 31 %
[=================================> ] 47 %
[============================================> ] 63 %
[========================================================> ] 80 %
[===================================================================> ] 96 %
Note that this output is shown one line below each other, but in a terminal emulator (I think also in Windows command line) it will be printed on the same line.
At the very end, don't forget to print a newline before printing more stuff.
If you want to remove the bar at the end, you have to overwrite it with spaces, to print something shorter like for example "Done."
.
Also, the same can of course be done using printf
in C; adapting the code above should be straight-forward.
Upvotes: 128
Reputation: 10487
You can print a carriage return character (\r
) to move the output "cursor" back to the beginning of the current line.
For a more sophisticated approach, take a look at something like ncurses (an API for console text-based interfaces).
Upvotes: 16