Merge pull request #51 from p-ranav/feature/43

Feature/43
This commit is contained in:
Pranav
2020-05-08 17:28:47 -05:00
committed by GitHub
6 changed files with 348 additions and 0 deletions

View File

@@ -38,6 +38,7 @@ make
## Table of Contents
* [Progress Bar](#progress-bar)
* [Indeterminate Progress Bar](#indeterminate-progress-bar)
* [Block Progress Bar](#block-progress-bar)
* [Multi Progress](#multiprogress)
* [Dynamic Progress](#dynamicprogress)
@@ -212,6 +213,61 @@ int main() {
}
```
## Indeterminate Progress Bar
You might have a use-case for a progress bar where the maximum amount of progress is unknown, e.g., you're downloading from a remote server that isn't advertising the total bytes.
Use an `indicators::IndeterminateProgressBar` for such cases. An `IndeterminateProgressBar` is similar to a regular progress bar except the total amount to progress towards is unknown. Ticking on this progress bar will happily run forever.
When you know progress is complete, simply call `bar.mark_as_completed()`.
<p align="center">
<img src="img/indeterminate_progress_bar.gif"/>
</p>
```cpp
#include <chrono>
#include <indicators/indeterminate_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <indicators/termcolor.hpp>
#include <thread>
int main() {
indicators::IndeterminateProgressBar bar{
indicators::option::BarWidth{40},
indicators::option::Start{"["},
indicators::option::Fill{"·"},
indicators::option::Lead{"<==>"},
indicators::option::End{"]"},
indicators::option::PostfixText{"Checking for Updates"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
};
indicators::show_console_cursor(false);
auto job = [&bar]() {
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
bar.mark_as_completed();
std::cout << termcolor::bold << termcolor::green
<< "System is up to date!\n" << termcolor::reset;
};
std::thread job_completion_thread(job);
// Update bar state
while (!bar.is_completed()) {
bar.tick();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
job_completion_thread.join();
indicators::show_console_cursor(true);
return 0;
}
```
## Block Progress Bar
Are you in need of a smooth block progress bar using [unicode block elements](https://en.wikipedia.org/wiki/Block_Elements)? Use `BlockProgressBar` instead of `ProgressBar`. Thanks to [this blog post](https://mike42.me/blog/2018-06-make-better-cli-progress-bars-with-unicode-block-characters) for making `BlockProgressBar` an easy addition to the library.

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -155,5 +155,30 @@ private:
std::string remainder;
};
class IndeterminateProgressScaleWriter {
public:
IndeterminateProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
const std::string &lead)
: os(os), bar_width(bar_width), fill(fill), lead(lead) {}
std::ostream &write(size_t progress) {
for (size_t i = 0; i < bar_width; ++i) {
if (i < progress)
os << fill;
else if (i == progress)
os << lead;
else
os << fill;
}
return os;
}
private:
std::ostream &os;
size_t bar_width = 0;
std::string fill;
std::string lead;
};
} // namespace details
} // namespace indicators

View File

@@ -0,0 +1,224 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <indicators/details/stream_helper.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
namespace indicators {
class IndeterminateProgressBar {
using Settings =
std::tuple<option::BarWidth, option::PrefixText, option::PostfixText, option::Start,
option::End, option::Fill, option::Lead,
option::MaxPostfixTextLen, option::Completed,
option::ForegroundColor, option::FontStyles>;
enum class Direction {
forward,
backward
};
Direction direction_{Direction::forward};
public:
template <typename... Args,
typename std::enable_if<details::are_settings_from_tuple<
Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit IndeterminateProgressBar(Args &&... args)
: settings_(details::get<details::ProgressBarOption::bar_width>(option::BarWidth{100},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(
option::PrefixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(
option::PostfixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::start>(option::Start{"["},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::end>(option::End{"]"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::fill>(option::Fill{"."},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::lead>(option::Lead{"<==>"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(option::Completed{false},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::unspecified}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::font_styles>(
option::FontStyles{std::vector<FontStyle>{}}, std::forward<Args>(args)...)) {
// starts with [<==>...........]
// progress_ = 0
// ends with [...........<==>]
// ^^^^^^^^^^^^^^^^^ bar_width
// ^^^^^^^^^^^^ (bar_width - len(lead))
// progress_ = bar_width - len(lead)
progress_ = 0;
max_progress_ = get_value<details::ProgressBarOption::bar_width>()
- get_value<details::ProgressBarOption::lead>().size()
+ get_value<details::ProgressBarOption::start>().size()
+ get_value<details::ProgressBarOption::end>().size();
}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
}
template <typename T, details::ProgressBarOption id>
void set_option(const details::Setting<T, id> &setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
}
void set_option(
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
}
}
void
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
}
}
void tick() {
{
std::lock_guard<std::mutex> lock{mutex_};
if (get_value<details::ProgressBarOption::completed>())
return;
progress_ += (direction_ == Direction::forward) ? 1 : -1;
if (direction_ == Direction::forward && progress_ == max_progress_) {
// time to go back
direction_ = Direction::backward;
} else if (direction_ == Direction::backward && progress_ == 0) {
direction_ = Direction::forward;
}
}
print_progress();
}
bool is_completed() {
return get_value<details::ProgressBarOption::completed>();
}
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
print_progress();
}
private:
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
template <details::ProgressBarOption id>
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
size_t progress_{0};
size_t max_progress_;
Settings settings_;
std::chrono::nanoseconds elapsed_;
std::mutex mutex_;
template <typename Indicator, size_t count> friend class MultiProgress;
template <typename Indicator> friend class DynamicProgress;
std::atomic<bool> multi_progress_mode_{false};
public:
void print_progress(bool from_multi_progress = false) {
std::lock_guard<std::mutex> lock{mutex_};
if (multi_progress_mode_ && !from_multi_progress) {
return;
}
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
details::set_stream_color(std::cout, get_value<details::ProgressBarOption::foreground_color>());
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
details::set_font_style(std::cout, style);
std::cout << get_value<details::ProgressBarOption::prefix_text>();
std::cout << get_value<details::ProgressBarOption::start>();
details::IndeterminateProgressScaleWriter writer{std::cout,
get_value<details::ProgressBarOption::bar_width>(),
get_value<details::ProgressBarOption::fill>(),
get_value<details::ProgressBarOption::lead>()};
writer.write(progress_);
std::cout << get_value<details::ProgressBarOption::end>();
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
std::cout << " " << get_value<details::ProgressBarOption::postfix_text>()
<< std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ')
<< "\r";
std::cout.flush();
if (get_value<details::ProgressBarOption::completed>() &&
!from_multi_progress) // Don't std::endl if calling from MultiProgress
std::cout << termcolor::reset << std::endl;
}
};
} // namespace indicators

View File

@@ -29,3 +29,6 @@ target_link_libraries(dynamic_progress PRIVATE indicators::indicators)
add_executable(max_progress max_progress.cpp)
target_link_libraries(max_progress PRIVATE indicators::indicators)
add_executable(indeterminate_progress_bar indeterminate_progress_bar.cpp)
target_link_libraries(indeterminate_progress_bar PRIVATE indicators::indicators)

View File

@@ -0,0 +1,40 @@
#include <chrono>
#include <indicators/indeterminate_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <indicators/termcolor.hpp>
#include <thread>
int main() {
indicators::IndeterminateProgressBar bar{
indicators::option::BarWidth{40},
indicators::option::Start{"["},
indicators::option::Fill{"·"},
indicators::option::Lead{"<==>"},
indicators::option::End{"]"},
indicators::option::PostfixText{"Checking for Updates"},
indicators::option::ForegroundColor{indicators::Color::yellow},
indicators::option::FontStyles{
std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
};
indicators::show_console_cursor(false);
auto job = [&bar]() {
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
bar.mark_as_completed();
std::cout << termcolor::bold << termcolor::green
<< "System is up to date!\n" << termcolor::reset;
};
std::thread job_completion_thread(job);
// Update bar state
while (!bar.is_completed()) {
bar.tick();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
job_completion_thread.join();
indicators::show_console_cursor(true);
return 0;
}