Barkeep
Small C++ header to display async animations, counters, progress bars, and status messages
Install / Use
/learn @oir/BarkeepREADME
Small, single C++ header to display async animations, counters, and progress bars.
Use it by including barkeep.h in your project.
barkeep strives to be non-intrusive.
barkeep also has python bindings.
💡 Documentation is a superset of what's below and easier to navigate.
-
Display a waiting animation with a message:
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/anim1-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/anim1-light.svg" width="700"> <img src="docs/rec/anim1-light.svg" width="700"> </picture>using namespace std::chrono_literals; namespace bk = barkeep; auto anim = bk::Animation({.message = "Working"}); /* do work */ std::this_thread::sleep_for(10s); anim->done(); -
Supports several styles:
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/anim2-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/anim2-light.svg" width="700"> <img src="docs/rec/anim2-light.svg" width="700"> </picture>auto anim = bk::Animation({.message = "Downloading...", .style = bk::Earth}); -
Display a counter to monitor a numeric variable while waiting:
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/counter-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/counter-light.svg" width="700"> <img src="docs/rec/counter-light.svg" width="700"> </picture>int work{0}; auto c = bk::Counter(&work, { .message = "Reading lines", .speed = 1., .speed_unit = "line/s" }); for (int i = 0; i < 505; i++) { std::this_thread::sleep_for(13ms); // read & process line work++; } c->done(); -
Display a progress bar to monitor a numeric variable and measure its completion by comparing against a total:
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/bar-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/bar-light.svg" width="700"> <img src="docs/rec/bar-light.svg" width="700"> </picture>int work{0}; auto bar = bk::ProgressBar(&work, { .total = 505, .message = "Reading lines", .speed = 1., .speed_unit = "line/s", }); for (int i = 0; i < 505; i++) { std::this_thread::sleep_for(13ms); // read & process line work++; } bar->done(); -
Bars can also be styled. Some styles have color:
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/bar-pip-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/bar-pip-light.svg" width="700"> <img src="docs/rec/bar-pip-light.svg" width="700"> </picture>int work{0}; auto bar = bk::ProgressBar(&work, { .total = 505, .message = "Reading lines", .speed = 1., .speed_unit = "line/s", .style = bk::ProgressBarStyle::Rich, }); for (int i = 0; i < 505; i++) { std::this_thread::sleep_for(13ms); // read & process line work++; } bar->done(); -
Displaying can be deferred with
.show = false, and explicitly invoked by callingshow(), instead of at construction time.Finishing the display can be done implicitly by the destructor, instead of calling
done()(this allows RAII-style use).The following are equivalent:
int work{0}; auto bar = bk::ProgressBar(&work, {.total = 505}); for (int i = 0; i < 505; i++) { std::this_thread::sleep_for(13ms); work++; } bar->done();int work; auto bar = bk::ProgressBar(&work, {.total = 505, .show = false}); work = 0; bar->show(); for (int i = 0; i < 505; i++) { std::this_thread::sleep_for(13ms); work++; } bar->done();int work{0}; { auto bar = bk::ProgressBar(&work, {.total = 505}); for (int i = 0; i < 505; i++) { std::this_thread::sleep_for(13ms); work++; } } -
Automatically iterate over a container with a progress bar display (instead of monitoring an explicit progress variable):
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/iter-bar-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/iter-bar-light.svg" width="700"> <img src="docs/rec/iter-bar-light.svg" width="700"> </picture> <blockquote> <details> <summary> Detail: IterableBar starts the display not at the time of construction, ... </summary>std::vector<float> v(300, 0); std::iota(v.begin(), v.end(), 1); // 1, 2, 3, ..., 300 float sum = 0; for (auto x : bk::IterableBar(v, {.message = "Summing", .interval = .02})) { std::this_thread::sleep_for(1.s/x); sum += x; } std::cout << "Sum: " << sum << std::endl;... but at the time of the first call to
begin(). Thus, it is possible to set it up prior to loop execution.Similarly, it ends the display not at the time of destruction, but at the first increment of the iterator past the end. Thus, even if the object stays alive after the loop, the display will be stopped.
Therefore, you could initialize it earlier than the loop execution, and destroy it late afterwards:
</details> </blockquote>std::vector<float> v(300, 0); std::iota(v.begin(), v.end(), 1); // 1, 2, 3, ..., 300 float sum = 0; bk::IterableBar bar(v, {.message = "Summing", .interval = .02}); // <-- At this point, display is not yet shown. // Thus, more work can be done here. for (auto x : bar) { // <-- Display starts showing. std::this_thread::sleep_for(1.s/x); sum += x; } // <-- Display stops here even if `bar` object is still alive. // Thus, more work can be done here. std::cout << "Sum: " << sum << std::endl; -
Use a function (e.g. lambda) to monitor progress, instead of a variable (credit: jh0x):
unsigned long total_area = 10000; unsigned long width = 0, height = 0; auto bar = bk::ProgressBar([&] { return width * height; }, { .total = total_area, .message = "Sweeping area", .speed = 1., }); while (width < 100 and height < 100) { std::this_thread::sleep_for(70ms); if (width < 100) { width++; } if (height < 100) { height++; } }Observe how a lambda is passed as the first argument as opposed to a variable like
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/lambda-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/lambda-light.svg" width="700"> <img src="docs/rec/lambda-light.svg" width="700"> </picture>&width.Such monitoring functions are concurrently invoked, see this section for what that might imply.
-
Combine diplays using
|operator to monitor multiple variables:std::atomic<size_t> sents{0}, toks{0}; auto bar = bk::ProgressBar(&sents, { .total = 1010, .message = "Sents", .show = false}) | bk::Counter(&toks, { .message = "Toks", .speed = 1., .speed_unit = "tok/s", .show = false}); bar->show(); for (int i = 0; i < 1010; i++) { // do work std::this_thread::sleep_for(13ms); sents++; toks += (1 + rand() % 5); } bar->done();(Observe the non-running initialization of components using
<picture> <source media="(prefers-color-scheme: dark)" srcset="docs/rec/composite-dark.svg" width="700"> <source media="(prefers-color-scheme: light)" srcset="docs/rec/composite-light.svg" width="700"> <img src="docs/rec/composite-light.svg" width="700"> </picture>.show = false, which is needed for composition.)Instead of using
|operator, you can also callComposite()with the components explicitly, which also accepts an additional string argument as the delimiter between the components. See the example below. -
If your display is multiline (ha
