diff --git a/README.md b/README.md index ede3482..cdc24f3 100644 --- a/README.md +++ b/README.md @@ -28,50 +28,49 @@ To create a progress bar, the total number of jobs need to be known. int n = 10; ProgressBar progress_bar(n); ``` - - Optionally, small descriptions can also be added to the progress bar + +Optionally, small descriptions can also be added to the progress bar. ```C++ int n = 10; -ProgressBar progress_bar(n,"Example 1"); +ProgressBar progress_bar(n, "Example 1"); ``` - - - + + Updating a progress bar ------------------------- - -Updates to the progress bar are made using the `Progressed(int)` method by passing the index of the job - + +Updates to the progress bar are made using the increment operators. + **Example 1:** ```C++ int n = 10; -ProgressBar progress_bar(n,"Example 1"); - +ProgressBar progress_bar(n, "Example 1"); + int job_index = 0; - + do_some_work(); - + job_index++; -progress_bar.Progressed(job_index); - +progress_bar += 1; + do_some_more_work(); - + job_index++; -progress_bar.Progressed(job_index); +progress_bar += 1; ``` - + **Example 2:** ```C++ int n = 100; -ProgressBar *bar = new ProgressBar(n, "Example 1"); - -for(int i=0;i<=n;++i){ - bar1->Progressed(i); - std::this_thread::sleep_for (std::chrono::milliseconds(10)); +ProgressBar bar1(n, "Example 1"); + +for (int i = 0; i <= n; ++i) { + ++bar1; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } ``` @@ -83,11 +82,11 @@ If progressing through a large job list, iterating through every update will slo **Example 3:** ```C++ int n = 100000; -ProgressBar *bar2 = new ProgressBar(n, "Example 3"); -bar2->SetFrequencyUpdate(10); - -for(int i=0;i<=n;++i){ - bar2->Progressed(i); +ProgressBar bar2(n, "Example 3"); +bar2.SetFrequencyUpdate(10); + +for (int i = 0; i <= n; ++i){ + ++bar2; } ``` @@ -99,12 +98,12 @@ The progress bar can be customised using the `SetStyle(const char*, const char*) **Example 4** ```C++ n = 1000; -ProgressBar *bar3 = new ProgressBar(n, "Example 4"); -bar3->SetFrequencyUpdate(10); -bar3->SetStyle("\u2588", "-"); - -for(int i=0;i<=n;++i){ - bar3->Progressed(i); +ProgressBar bar3(n, "Example 4"); +bar3.SetFrequencyUpdate(10); +bar3.SetStyle("\u2588", "-"); + +for (int i = 0; i <= n; ++i) { + ++bar3; } ``` @@ -112,66 +111,61 @@ for(int i=0;i<=n;++i){ Main Example ========= - ```C++ -#include "progress_bar.hpp" #include #include #include -int main(){ - - int n; - - /// Example 1 /// - - n = 100; - ProgressBar *bar1 = new ProgressBar(n, "Example 1"); - - for(int i=0;i<=n;++i){ - bar1->Progressed(i); - std::this_thread::sleep_for (std::chrono::milliseconds(10)); - } +#include "progress_bar.hpp" - /// Example 2 /// - - n = 1000; - ProgressBar *bar2 = new ProgressBar(n, "Example 2"); - bar2->SetFrequencyUpdate(10); - bar2->SetStyle("|","-"); - //bar2->SetStyle("\u2588", "-"); for linux - - std::cout << std::endl; - for(int i=0;i<=n;++i){ - bar2->Progressed(i); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - n = 5; - ProgressBar bar3(n); - bar3.Progressed(0); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(1); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(2); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(3); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(4); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(5); - // following tests exception error - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(6); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(7); - return 0; -} -``` +int main() { + int n; + /// Example 1 /// + n = 100; + ProgressBar bar1(n, "Example 1"); + for (int i = 0; i <= n; ++i) { + ++bar1; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + /// Example 2 /// + n = 1000; + ProgressBar bar2(n, "Example 2"); + bar2.SetFrequencyUpdate(10); + bar2.SetStyle("|", "-"); + //bar2.SetStyle("\u2588", "-"); for linux + + std::cout << std::endl; + for (int i = 0; i <= n; ++i) { + ++bar2; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + n = 5; + ProgressBar bar3(n); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + // following tests exception error + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + + return 0; +} +``` diff --git a/main.cpp b/main.cpp index 02648d8..06122e2 100644 --- a/main.cpp +++ b/main.cpp @@ -3,52 +3,47 @@ #include #include -int main(){ - - int n; - +int main() { /// Example 1 /// + { + int n = 100; + ProgressBar bar1(n, "Example 1"); - n = 100; - ProgressBar *bar1 = new ProgressBar(n, "Example 1"); - - for(int i=0;i<=n;++i){ - bar1->Progressed(i); - std::this_thread::sleep_for (std::chrono::milliseconds(10)); - } + for (int i = 1; i <= n; ++i) { + bar1 += 1; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } /// Example 2 /// + { + int n = 1000; + ProgressBar bar2(n, "Example 2"); + bar2.SetFrequencyUpdate(10); + bar2.SetStyle('|','-'); + //bar2.SetStyle("\u2588", "-"); for linux + for (int i = 1; i <= n; ++i) { + bar2 += 1; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } - n = 1000; - ProgressBar *bar2 = new ProgressBar(n, "Example 2"); - bar2->SetFrequencyUpdate(10); - bar2->SetStyle("|","-"); - //bar2->SetStyle("\u2588", "-"); for linux - - std::cout << std::endl; - for(int i=0;i<=n;++i){ - bar2->Progressed(i); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - n = 5; - ProgressBar bar3(n); - bar3.Progressed(0); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(1); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(2); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(3); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(4); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(5); - // following tests exception error - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(6); - std::this_thread::sleep_for (std::chrono::milliseconds(200)); - bar3.Progressed(7); + ProgressBar bar3(5); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + // following tests exception error + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + ++bar3; return 0; } diff --git a/progress_bar.cpp b/progress_bar.cpp index 745844a..86a5498 100644 --- a/progress_bar.cpp +++ b/progress_bar.cpp @@ -1,104 +1,204 @@ #include "progress_bar.hpp" -ProgressBar::ProgressBar() {} +#include +#include +#include +#include +#include +#include +#include + +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) + #include + #if !defined(_POSIX_VERSION) + #include + #endif +#else + #include +#endif + +const size_t kMessageSize = 20; +const double kTotalPercentage = 100.0; +const size_t kCharacterWidthPercentage = 7; +const int kDefaultConsoleWidth = 100; +const int kMaxBarWidth = 120; + + +bool to_terminal(const std::ostream &os) { +#if _WINDOWS + if (os.rdbuf() == std::cout.rdbuf() && !_isatty(_fileno(stdout))) + return false; + if (os.rdbuf() == std::cerr.rdbuf() && !_isatty(_fileno(stderr))) + return false; +#else + if (os.rdbuf() == std::cout.rdbuf() && !isatty(fileno(stdout))) + return false; + if (os.rdbuf() == std::cerr.rdbuf() && !isatty(fileno(stderr))) + return false; +#endif + return true; +} + +ProgressBar::ProgressBar(uint64_t total, + const std::string &description, + std::ostream &out_, + bool silent) + : silent_(silent), total_(total), description_(description) { + + if (silent_) + return; -ProgressBar::ProgressBar(unsigned long n_, const char* description_, std::ostream& out_){ - - n = n_; - frequency_update = n_; - description = description_; + frequency_update = std::max(static_cast(1), total_ / 1000); out = &out_; - - unit_bar = "="; - unit_space = " "; - desc_width = std::strlen(description); // character width of description field + if ((logging_mode_ = !to_terminal(*out))) + *out << description_ << std::endl; + + description_.resize(kMessageSize, ' '); + + ShowProgress(0); + if (progress_ == total_) + *out << std::endl; } -void ProgressBar::SetFrequencyUpdate(unsigned long frequency_update_){ - - if(frequency_update_ > n){ - frequency_update = n; // prevents crash if freq_updates_ > n_ - } - else{ - frequency_update = frequency_update_; - } +ProgressBar::~ProgressBar() { + if (progress_ != total_) { + // this is not supposed to happen, but may be useful for debugging + ShowProgress(progress_); + if (!silent_) + *out << "\n"; + } } -void ProgressBar::SetStyle(const char* unit_bar_, const char* unit_space_){ - - unit_bar = unit_bar_; - unit_space = unit_space_; +void ProgressBar::SetFrequencyUpdate(uint64_t frequency_update_) { + std::lock_guard lock(mu_); + + if(frequency_update_ > total_){ + frequency_update = total_; // prevents crash if freq_updates_ > total_ + } else{ + frequency_update = frequency_update_; + } } -int ProgressBar::GetConsoleWidth(){ +void ProgressBar::SetStyle(char unit_bar, char unit_space) { + std::lock_guard lock(mu_); - int width; + unit_bar_ = unit_bar; + unit_space_ = unit_space; +} - #ifdef _WINDOWS - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); - width = csbi.srWindow.Right - csbi.srWindow.Left; - #else - struct winsize win; - ioctl(0, TIOCGWINSZ, &win); - width = win.ws_col; - #endif +int ProgressBar::GetConsoleWidth() const { + int width = kDefaultConsoleWidth; + +#ifdef _WINDOWS + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + width = csbi.srWindow.Right - csbi.srWindow.Left; +#else + struct winsize win; + if (ioctl(0, TIOCGWINSZ, &win) != -1) + width = win.ws_col; +#endif return width; } -int ProgressBar::GetBarLength(){ +int ProgressBar::GetBarLength() const { + // get console width and according adjust the length of the progress bar + return std::min(GetConsoleWidth(), kMaxBarWidth) + - 9 + - description_.size() + - kCharacterWidthPercentage + - std::floor(std::log10(std::max((uint64_t)2, total_)) + 1) * 2; +} - // get console width and according adjust the length of the progress bar +std::string get_progress_summary(double progress_ratio) { + std::string buffer = std::string(kCharacterWidthPercentage, ' '); - int bar_length = static_cast((GetConsoleWidth() - desc_width - CHARACTER_WIDTH_PERCENTAGE) / 2.); + // in some implementations, snprintf always appends null terminal character + snprintf((char *)buffer.data(), kCharacterWidthPercentage, + "%5.1f%%", progress_ratio * kTotalPercentage); - return bar_length; + // erase the last null terminal character + buffer.pop_back(); + return buffer; } -void ProgressBar::ClearBarField(){ +void ProgressBar::ShowProgress(uint64_t progress) const { + if (silent_) + return; + + std::lock_guard lock(mu_); + + // calculate percentage of progress + double progress_ratio = total_ ? static_cast(progress) / total_ + : 1.0; + assert(progress_ratio >= 0.0); + assert(progress_ratio <= 1.0); + + if (logging_mode_) { + // get current time + auto now = std::chrono::system_clock::now(); + std::time_t time = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast( + now.time_since_epoch()) % 1000; + std::stringstream os; + os << std::put_time(std::localtime(&time), "[%F %T.") + << std::setfill('0') << std::setw(3) << ms.count() << "]\t" + << get_progress_summary(progress_ratio) + << ", " + std::to_string(progress) + "/" + std::to_string(total_) + '\n'; + *out << os.str() << std::flush; + return; + } + + try { + // clear previous progressbar + *out << std::string(buffer_.size(), ' ') + '\r' << std::flush; + buffer_.clear(); - for(int i=0;i n) throw idx_; +ProgressBar& ProgressBar::operator++() { + return (*this) += 1; +} - // determines whether to update the progress bar from frequency_update - if ((idx_ != n) && (idx_ % (n/frequency_update) != 0)) return; +ProgressBar& ProgressBar::operator+=(uint64_t delta) { + if (silent_ || !delta) + return *this; - // calculate the size of the progress bar - int bar_size = GetBarLength(); - - // calculate percentage of progress - double progress_percent = idx_* TOTAL_PERCENTAGE/n; - - // calculate the percentage value of a unit bar - double percent_per_unit_bar = TOTAL_PERCENTAGE/bar_size; - - // display progress bar - *out << " " << description << " ["; - - for(int bar_length=0;bar_length<=bar_size-1;++bar_length){ - if(bar_length*percent_per_unit_bar -#include -#include - -#define TOTAL_PERCENTAGE 100.0 -#define CHARACTER_WIDTH_PERCENTAGE 4 - -class ProgressBar{ - -public: - - ProgressBar(); - ProgressBar(unsigned long n_, const char *description_="", std::ostream& out_=std::cerr); - - void SetFrequencyUpdate(unsigned long frequency_update_); - void SetStyle(const char* unit_bar_, const char* unit_space_); - - void Progressed(unsigned long idx_); - -private: - - unsigned long n; - unsigned int desc_width; - unsigned long frequency_update; - std::ostream* out; - - const char *description; - const char *unit_bar; - const char *unit_space; - - void ClearBarField(); - int GetConsoleWidth(); - int GetBarLength(); - +#include +#include +#include + + +class ProgressBar { + public: + ProgressBar(uint64_t total, + const std::string &description = "", + std::ostream &out = std::cerr, + bool silent = false); + + ~ProgressBar(); + + void SetFrequencyUpdate(uint64_t frequency_update_); + void SetStyle(char unit_bar, char unit_space); + + ProgressBar& operator++(); + ProgressBar& operator+=(uint64_t delta); + + private: + ProgressBar(const ProgressBar &) = delete; + ProgressBar& operator=(const ProgressBar &) = delete; + + void ShowProgress(uint64_t progress) const; + int GetConsoleWidth() const; + int GetBarLength() const; + + bool silent_; + bool logging_mode_; + uint64_t total_; + std::atomic progress_ = {0}; + uint64_t frequency_update; + std::ostream *out; + mutable std::mutex mu_; + mutable std::string buffer_; + + std::string description_; + char unit_bar_ = '='; + char unit_space_ = ' '; }; -#endif +#endif // _PROGRESS_BAR_