From 01c5aa5abd54ad18d66a642e215432216fbcccae Mon Sep 17 00:00:00 2001 From: Giovanni De Toni Date: Tue, 30 May 2017 10:19:35 +0200 Subject: [PATCH] [ProgressBar] Add progress bar code on top of its "skeleton". * Add print_progress() and print_end() code; * The progress bar can now adjust its size depending on the screen dimension (should work also on windows); * The progress bar must be enabled by the user by using SGIO enable_progress(); --- src/shogun/base/progress.h | 257 ++++++++++++++++++++++++++++++++----- src/shogun/base/range.h | 223 ++++++++++++++++---------------- 2 files changed, 335 insertions(+), 145 deletions(-) diff --git a/src/shogun/base/progress.h b/src/shogun/base/progress.h index b8261d34d0f..28def189e05 100644 --- a/src/shogun/base/progress.h +++ b/src/shogun/base/progress.h @@ -12,6 +12,15 @@ #include #include #include +#include +#include + +#if WIN32 +#include +#else +#include +#include +#endif namespace shogun { @@ -33,21 +42,35 @@ namespace shogun * * @param io SGIO object */ - ProgressPrinter(const SGIO& io) - : m_io(io), m_prefix("PROGRESS: "), m_mode(UTF8) + ProgressPrinter( + const SGIO& io, float64_t max_value, float64_t min_value) + : m_io(io), m_max_value(max_value), m_min_value(min_value), + m_prefix("PROGRESS: "), m_mode(UTF8), m_last_progress(0), + m_last_progress_time(0), m_progress_start_time(0) { } - ProgressPrinter(const SGIO& io, const std::string& prefix) - : m_io(io), m_prefix(prefix), m_mode(UTF8) + ProgressPrinter( + const SGIO& io, float64_t max_value, float64_t min_value, + const std::string& prefix) + : m_io(io), m_max_value(max_value), m_min_value(min_value), + m_prefix(prefix), m_mode(UTF8), m_last_progress(0), + m_last_progress_time(0), m_progress_start_time(0) { } - ProgressPrinter(const SGIO& io, const SG_PRG_MODE mode) - : m_io(io), m_prefix("PROGRESS: "), m_mode(mode) + ProgressPrinter( + const SGIO& io, float64_t max_value, float64_t min_value, + const SG_PRG_MODE mode) + : m_io(io), m_max_value(max_value), m_min_value(min_value), + m_prefix("PROGRESS: "), m_mode(mode), m_last_progress(0), + m_last_progress_time(0), m_progress_start_time(0) { } ProgressPrinter( - const SGIO& io, const std::string& prefix, const SG_PRG_MODE mode) - : m_io(io), m_prefix(prefix), m_mode(mode) + const SGIO& io, float64_t max_value, float64_t min_value, + const std::string& prefix, const SG_PRG_MODE mode) + : m_io(io), m_max_value(max_value), m_min_value(min_value), + m_prefix(prefix), m_mode(mode), m_last_progress(0), + m_last_progress_time(0), m_progress_start_time(0) { } ~ProgressPrinter() @@ -55,34 +78,183 @@ namespace shogun } /** Print the progress bar */ - void print_progress() const + void print_progress(float64_t current_value) const { - m_io.message(MSG_MESSAGEONLY, "", "", -1, "TEST\n"); + // Check if the progress was enabled + if (!m_io.get_show_progress()) + return; + + if (m_max_value <= m_min_value) + return; + + // Check for terminal dimension. This is for provide + // a minimal resize functionality. + set_screen_size(); + + float64_t difference = m_max_value - m_min_value, v = -1, + estimate = 0, total_estimate = 0; + float64_t size_chunk = -1; + + // Check if we have enough space to show the progress bar + // Use only a fraction of it to account for the size of the + // time displayed (decimals and integer). + int32_t progress_bar_space = + (m_columns_num - 50 - m_prefix.length()) * 0.9; + REQUIRE( + progress_bar_space > 0, + "Not enough terminal space to show the progress bar!\n") + + char str[1000]; + float64_t runtime = CTime::get_curtime(); + + if (difference > 0.0) + v = 100 * (current_value - m_min_value + 1) / + (m_max_value - m_min_value + 1); + + // Set up chunk size + size_chunk = difference / (double_t)progress_bar_space; + + if (m_last_progress == 0) + { + m_last_progress_time = runtime; + m_progress_start_time = runtime; + m_last_progress = v; + } + else + { + m_last_progress = v - 1e-6; + + if ((v != 100.0) && (runtime - m_last_progress_time < 0.5)) + { + // This is made to display correctly the percentage + // if the algorithm execution is too fast + if (current_value >= m_max_value - 1) + { + v = 100; + m_last_progress = v - 1e-6; + snprintf( + str, sizeof(str), "%%s %.2f %%1.1f " + "seconds remaining %%1.1f " + "seconds total\r"); + m_io.message( + MSG_MESSAGEONLY, "", "", -1, str, m_prefix.c_str(), + v, estimate, total_estimate); + } + return; + } + + m_last_progress_time = runtime; + estimate = (1 - v / 100) * + (m_last_progress_time - m_progress_start_time) / + (v / 100); + total_estimate = + (m_last_progress_time - m_progress_start_time) / (v / 100); + } + + /** Print the actual progress bar to screen **/ + m_io.message(MSG_MESSAGEONLY, "", "", -1, "%s |", m_prefix.c_str()); + for (index_t i = 1; i < progress_bar_space; i++) + { + if (current_value > i * size_chunk) + { + m_io.message( + MSG_MESSAGEONLY, "", "", -1, "%s", + get_pb_char().c_str()); + } + else + { + m_io.message(MSG_MESSAGEONLY, "", "", -1, " "); + } + } + m_io.message(MSG_MESSAGEONLY, "", "", -1, "| %.2f\%", v); + + if (estimate > 120) + { + snprintf( + str, sizeof(str), + " %%1.1f minutes remaining %%1.1f minutes total\r"); + m_io.message( + MSG_MESSAGEONLY, "", "", -1, str, estimate / 60, + total_estimate / 60); + } + else + { + snprintf( + str, sizeof(str), + " %%1.1f seconds remaining %%1.1f seconds total\r"); + m_io.message( + MSG_MESSAGEONLY, "", "", -1, str, estimate, total_estimate); + } + + // If we arrive to the end, we print a new line (fancier output) + if (current_value >= m_max_value) + m_io.message(MSG_MESSAGEONLY, "", "", -1, "\n"); } /** Print the progress bar end */ void print_end() const { - m_io.message(MSG_MESSAGEONLY, "", "", -1, "100\n"); + // Check if the progress was enabled + if (!m_io.get_show_progress()) + return; + + print_progress(m_max_value); } private: + + std::string get_pb_char() const + { + switch (m_mode) + { + case ASCII: + return m_ascii_char; + case UTF8: + return m_utf8_char; + default: + return m_ascii_char; + } + } + + void set_screen_size() const + { +#if WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + m_columns_num = csbi.srWindow.Right - csbi.srWindow.Left + 1; + m_rows_num = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; +#else + struct winsize wind; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &wind); + m_columns_num = wind.ws_col; + m_rows_num = wind.ws_row; +#endif + } + /** IO object */ SGIO m_io; /** Prefix which will be printed before the progress bar */ std::string m_prefix; /** Progres bar's char mode */ SG_PRG_MODE m_mode; + /** ASCII char */ + std::string m_ascii_char = "#"; + /** UTF8 char */ + std::string m_utf8_char = "\u2588"; + /* Screen column number*/ + mutable int32_t m_columns_num; + /* Screen row number*/ + mutable int32_t m_rows_num; /** Maxmimum value */ float64_t m_max_value; /** Minimum value */ float64_t m_min_value; /** Last progress time */ - float64_t m_last_progress_time; + mutable float64_t m_last_progress_time; /** Progress start time */ - float64_t m_progress_start_time; + mutable float64_t m_progress_start_time; /** Last progress */ - float64_t m_last_progress; + mutable float64_t m_last_progress; }; /** @class Helper class to show a progress bar given a range. @@ -102,24 +274,32 @@ namespace shogun */ PRange(Range range, const SGIO& io) : m_range(range) { - m_printer = std::make_shared(io); + set_up_range(); + m_printer = + std::make_shared(io, end_range, begin_range); } PRange(Range range, const SGIO& io, const SG_PRG_MODE mode) - : m_range(range) + : m_range(range) { - m_printer = std::make_shared(io, mode); + set_up_range(); + m_printer = std::make_shared( + io, end_range, begin_range, mode); } PRange(Range range, const SGIO& io, const std::string prefix) - : m_range(range) + : m_range(range) { - m_printer = std::make_shared(io, prefix); + set_up_range(); + m_printer = std::make_shared( + io, end_range, begin_range, prefix); } PRange( - Range range, const SGIO& io, const std::string prefix, - const SG_PRG_MODE mode) - : m_range(range) + Range range, const SGIO& io, const std::string prefix, + const SG_PRG_MODE mode) + : m_range(range) { - m_printer = std::make_shared(io, prefix, mode); + set_up_range(); + m_printer = std::make_shared( + io, end_range, begin_range, prefix, mode); } /** @class Wrapper for Range::Iterator spawned by @ref PRange. */ @@ -127,17 +307,17 @@ namespace shogun { public: PIterator( - typename Range::Iterator value, - std::shared_ptr shrd_ptr) - : m_value(value), m_printer(shrd_ptr) + typename Range::Iterator value, + std::shared_ptr shrd_ptr) + : m_value(value), m_printer(shrd_ptr) { } PIterator(const PIterator& other) - : m_value(other.m_value), m_printer(other.m_printer) + : m_value(other.m_value), m_printer(other.m_printer) { } PIterator(PIterator&& other) - : m_value(other.m_value), m_printer(other.m_printer) + : m_value(other.m_value), m_printer(other.m_printer) { } PIterator& operator=(const PIterator&) = delete; @@ -145,7 +325,7 @@ namespace shogun { // Every time we update the iterator we print // also the updated progress bar - m_printer->print_progress(); + m_printer->print_progress((*m_value)); m_value++; return *this; } @@ -174,7 +354,8 @@ namespace shogun } private: - /* The ProgressPrinter object which will be used to show the progress bar*/ + /* The ProgressPrinter object which will be used to show the + * progress bar*/ std::shared_ptr m_printer; /* The wrapped range */ typename Range::Iterator m_value; @@ -193,10 +374,18 @@ namespace shogun } private: + void set_up_range() + { + begin_range = *(m_range.begin()); + end_range = *(m_range.end()); + } + /** Range we iterate over */ Range m_range; /** Observer that will print the actual progress bar */ std::shared_ptr m_printer; + float64_t begin_range; + float64_t end_range; }; /** Creates @ref PRange given a range. @@ -209,9 +398,9 @@ namespace shogun * @param io SGIO object */ template - inline PRange progress(Range range, const SGIO& io) + inline PRange progress(Range range, const SGIO& io, SG_PRG_MODE mode=UTF8) { - return PRange(range, io); + return PRange(range, io, mode); } /** Creates @ref PRange given a range that uses the global SGIO @@ -223,9 +412,9 @@ namespace shogun * @param range range used */ template - inline PRange progress(Range range) + inline PRange progress(Range range, SG_PRG_MODE mode=UTF8) { - return PRange(range, *sg_io); + return PRange(range, *sg_io, mode); } }; #endif /* __SG_PROGRESS_H__ */ \ No newline at end of file diff --git a/src/shogun/base/range.h b/src/shogun/base/range.h index 1591dad6330..62a238fde6c 100644 --- a/src/shogun/base/range.h +++ b/src/shogun/base/range.h @@ -1,126 +1,127 @@ #ifndef __SG_RANGE_H__ #define __SG_RANGE_H__ -#include #include +#include namespace shogun { - /** @class Helper class to spawn range iterator. - * - * Useful for C++11-style for loops: - * - * @code - * for (auto i : Range(3, 10)) { ... } - * @endcode - */ - template - class Range - { - public: - /** Creates range with specified bounds. - * Assumes rbegin < rend. - * - * @param rbegin lower bound of range - * @param rend upper bound of range (excluding) - */ - Range(T rbegin, T rend) : m_begin(rbegin), m_end(rend) - { - } + /** @class Helper class to spawn range iterator. + * + * Useful for C++11-style for loops: + * + * @code + * for (auto i : Range(3, 10)) { ... } + * @endcode + */ + template + class Range + { + public: + /** Creates range with specified bounds. + * Assumes rbegin < rend. + * + * @param rbegin lower bound of range + * @param rend upper bound of range (excluding) + */ + Range(T rbegin, T rend) : m_begin(rbegin), m_end(rend) + { + } + + /** @class Iterator spawned by @ref Range. */ + class Iterator : public std::iterator + { + public: + Iterator(T value) : m_value(value) + { + } + Iterator(const Iterator& other) : m_value(other.m_value) + { + } + Iterator(Iterator&& other) : m_value(other.m_value) + { + } + Iterator& operator=(const Iterator&) = delete; + Iterator& operator++() + { + m_value++; + return *this; + } + Iterator& operator++(int) + { + Iterator tmp(*this); + ++*this; + return tmp; + } + T operator*() + { + return m_value; + } + bool operator!=(const Iterator& other) + { + return this->m_value != other.m_value; + } + bool operator==(const Iterator& other) + { + return this->m_value == other.m_value; + } - /** @class Iterator spawned by @ref Range. */ - class Iterator : public std::iterator - { - public: - Iterator(T value) : m_value(value) - { - } - Iterator(const Iterator& other) : m_value(other.m_value) - { - } - Iterator(Iterator&& other) : m_value(other.m_value) - { - } - Iterator& operator=(const Iterator&) = delete; - Iterator& operator++() - { - m_value++; - return *this; - } - Iterator& operator++(int) - { - Iterator tmp(*this); - ++*this; - return tmp; - } - T operator*() - { - return m_value; - } - bool operator!=(const Iterator& other) - { - return this->m_value != other.m_value; - } - bool operator==(const Iterator& other) - { - return this->m_value == other.m_value; - } - private: - T m_value; - }; - /** Create iterator that corresponds to the start of range. - * - * Usually called through for-loop syntax. - */ - Iterator begin() const - { - return Iterator(m_begin); - } - /** Create iterator that corresponds to the end of range. - * - * Usually called through for-loop syntax. - */ - Iterator end() const - { - return Iterator(m_end); - } - private: - /** begin of range */ - T m_begin; - /** end of range */ - T m_end; - }; + private: + T m_value; + }; + /** Create iterator that corresponds to the start of range. + * + * Usually called through for-loop syntax. + */ + Iterator begin() const + { + return Iterator(m_begin); + } + /** Create iterator that corresponds to the end of range. + * + * Usually called through for-loop syntax. + */ + Iterator end() const + { + return Iterator(m_end); + } - /** Creates @ref Range with specified upper bound. - * - * @code - * for (auto i : range(100)) { ... } - * @endcode - * - * @param rend upper bound of range (excluding) - */ - template - inline Range range(T rend) - { - return Range(0, rend); - } + private: + /** begin of range */ + T m_begin; + /** end of range */ + T m_end; + }; - /** Creates @ref Range with specified bounds. - * - * @code - * for (auto i : range(0, 100)) { ... } - * @endcode - * - * @param rbegin lower bound of range - * @param rend upper bound of range (excluding) - */ - template - inline Range range(T rbegin, T rend) - { - return Range(rbegin, rend); - } + /** Creates @ref Range with specified upper bound. + * + * @code + * for (auto i : range(100)) { ... } + * @endcode + * + * @param rend upper bound of range (excluding) + */ + template + inline Range range(T rend) + { + return Range(0, rend); + } + /** Creates @ref Range with specified bounds. + * + * @code + * for (auto i : range(0, 100)) { ... } + * @endcode + * + * @param rbegin lower bound of range + * @param rend upper bound of range (excluding) + */ + template + inline Range range(T rbegin, T rend) + { + return Range(rbegin, rend); + } } #endif /* __SG_RANGE_H__ */