SkillAgentSearch skills...

Barkeep

Small C++ header to display async animations, counters, progress bars, and status messages

Install / Use

/learn @oir/Barkeep
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="docs/img/barkeep-logo-white.png" width="300"> <source media="(prefers-color-scheme: light)" srcset="docs/img/barkeep-logo-black.png" width="300"> <img src="docs/img/barkeep-logo-black.png" width="300"> </picture> </p>

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.

<div> <picture> <source media="(prefers-color-scheme: dark)" srcset="docs/img/C++-light.svg" height="22"> <source media="(prefers-color-scheme: light)" srcset="docs/img/C++-dark.svg" height="22"> <img src="docs/img/C++-dark.svg" height="22"> </picture> <a href="https://github.com/oir/barkeep/actions/workflows/build-test.yml/badge.svg"><img src="https://github.com/oir/barkeep/actions/workflows/build-test.yml/badge.svg" alt="Build status"></a> <a href="https://coveralls.io/github/oir/barkeep?branch=main"><img src="https://coveralls.io/repos/github/oir/barkeep/badge.svg?branch=main" alt="Coverage status"></a> <a href=""><img src="https://img.shields.io/badge/std-c++20-blue.svg" alt="c++20"></a> <a href=""><img src="docs/img/C++-include.svg"></a> </div> <div> <picture> <source media="(prefers-color-scheme: dark)" srcset="docs/img/python-light.svg" height="22"> <source media="(prefers-color-scheme: light)" srcset="docs/img/python-dark.svg" height="22"> <img src="docs/img/python-dark.svg" height="22"> </picture> <a href="https://github.com/oir/barkeep/actions/workflows/build-wheels.yml/badge.svg"><img src="https://github.com/oir/barkeep/actions/workflows/build-wheels.yml/badge.svg" alt="Build status"></a> <a href="https://pypi.python.org/pypi/barkeep"><img src="https://img.shields.io/badge/python-3.9_|_3.10_|_3.11_|_3.12-blue.svg" alt="pypi"></a> <a href=""><img src="docs/img/pip-install.svg"></a> </div>

💡 Documentation is a superset of what's below and easier to navigate.


  • Display a waiting animation with a message:

    using namespace std::chrono_literals;
    namespace bk = barkeep;
    
    auto anim = bk::Animation({.message = "Working"});
    /* do work */ std::this_thread::sleep_for(10s);
    anim->done();
    
    <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>
  • Supports several styles:

    auto anim = bk::Animation({.message = "Downloading...", .style = bk::Earth});
    
    <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>
  • Display a counter to monitor a numeric variable while waiting:

    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();
    
    <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>
  • Display a progress bar to monitor a numeric variable and measure its completion by comparing against a total:

    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();
    
    <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>
  • Bars can also be styled. Some styles have color:

    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();
    
    <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>
  • Displaying can be deferred with .show = false, and explicitly invoked by calling show(), 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):

      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;
    
    <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>

    ... 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:

    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;
    
    </details> </blockquote>
  • 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 &width.

    <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>

    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 .show = false, which is needed for composition.)

    <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>

    Instead of using | operator, you can also call Composite() 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

View on GitHub
GitHub Stars226
CategoryDevelopment
Updated7d ago
Forks2

Languages

C++

Security Score

100/100

Audited on Mar 19, 2026

No findings