├── images └── example.gif ├── LICENSE ├── README.md ├── test.cpp └── tqdm.h /images/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aminnj/cpptqdm/HEAD/images/example.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nick Amin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## cpptqdm 2 | 3 | Python `tqdm` is nice. Need something similar for C++. That's this. 4 | 5 | #### Simple usage 6 | ```cpp 7 | #include "tqdm.h" 8 | 9 | tqdm bar; 10 | for(int i = 0; i < N; i++) { 11 | bar.progress(i, N); 12 | // stuff 13 | } 14 | bar.finish(); 15 | ``` 16 | 17 | #### Looks like 18 | ``` 19 | █████████████████▍ | 46.2% [4200000 | 103.66 kHz | 35s<47s] 20 | ``` 21 | 22 | #### See it live 23 | ```bash 24 | g++ test.cpp -std=c++11 && ./a.out 25 | ``` 26 | 27 | ![example](images/example.gif) 28 | 29 | ### FAQ 30 | 31 | #### Won't this slow down my loops? 32 | 33 | If your loops are faster than ~200MHz, then maybe! 34 | 35 | #### Themes? 36 | 37 | You bet. `set_theme_basic(), set_theme_line(), set_theme_circles()`. 38 | 39 | #### *For fun*, what if I wanted to use this in python? 40 | 41 | If you have ROOT, you can do the following. Note that 42 | due to the fact it uses ROOT to call C++ code in 43 | Python, loops faster than 1kHz start to get slowed 44 | down by the overhead. 45 | ```python 46 | import time 47 | import ROOT as r 48 | 49 | r.gROOT.ProcessLine(".L tqdm.h") 50 | 51 | bar = r.tqdm() 52 | 53 | N = 10000 54 | for i in range(N): 55 | bar.progress(i,N) 56 | time.sleep(0.001) 57 | ``` 58 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include "tqdm.h" 2 | 3 | int main() { 4 | 5 | int N = 2000; 6 | tqdm bar; 7 | 8 | std::cout << "Overhead of loop only:" << std::endl; 9 | for(int i = 0; i < 100000000; i++) { 10 | bar.progress(i, 100000000); 11 | } 12 | bar.finish(); 13 | 14 | 15 | std::cout << "Basic:" << std::endl; 16 | bar.reset(); 17 | bar.set_theme_basic(); 18 | for(int i = 0; i < N; i++) { 19 | bar.progress(i, N); 20 | usleep(1000); 21 | } 22 | bar.finish(); 23 | 24 | std::cout << "Braille:" << std::endl; 25 | bar.reset(); 26 | bar.set_theme_braille(); 27 | for(int i = 0; i < N; i++) { 28 | bar.progress(i, N); 29 | usleep(3000); 30 | } 31 | bar.finish(); 32 | 33 | std::cout << "Line:" << std::endl; 34 | bar.reset(); 35 | bar.set_theme_line(); 36 | for(int i = 0; i < N; i++) { 37 | bar.progress(i, N); 38 | usleep(3000); 39 | } 40 | bar.finish(); 41 | 42 | std::cout << "Circles:" << std::endl; 43 | bar.reset(); 44 | bar.set_theme_circle(); 45 | for(int i = 0; i < N; i++) { 46 | bar.progress(i, N); 47 | usleep(3000); 48 | } 49 | bar.finish(); 50 | 51 | bar.reset(); 52 | std::cout << "Vertical bars:" << std::endl; 53 | bar.reset(); 54 | bar.set_theme_vertical(); 55 | for(int i = 0; i < N; i++) { 56 | bar.progress(i, N); 57 | usleep(3000); 58 | } 59 | bar.finish(); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /tqdm.h: -------------------------------------------------------------------------------- 1 | #ifndef TQDM_H 2 | #define TQDM_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class tqdm { 16 | private: 17 | // time, iteration counters and deques for rate calculations 18 | std::chrono::time_point t_first = std::chrono::system_clock::now(); 19 | std::chrono::time_point t_old = std::chrono::system_clock::now(); 20 | int n_old = 0; 21 | std::vector deq_t; 22 | std::vector deq_n; 23 | int nupdates = 0; 24 | int total_ = 0; 25 | int period = 1; 26 | unsigned int smoothing = 50; 27 | bool use_ema = true; 28 | float alpha_ema = 0.1; 29 | 30 | std::vector bars = {" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"}; 31 | 32 | bool in_screen = (system("test $STY") == 0); 33 | bool in_tmux = (system("test $TMUX") == 0); 34 | bool is_tty = isatty(1); 35 | bool use_colors = true; 36 | bool color_transition = true; 37 | int width = 40; 38 | 39 | std::string right_pad = "▏"; 40 | std::string label = ""; 41 | 42 | void hsv_to_rgb(float h, float s, float v, int& r, int& g, int& b) { 43 | if (s < 1e-6) { 44 | v *= 255.; 45 | r = v; g = v; b = v; 46 | } 47 | int i = (int)(h*6.0); 48 | float f = (h*6.)-i; 49 | int p = (int)(255.0*(v*(1.-s))); 50 | int q = (int)(255.0*(v*(1.-s*f))); 51 | int t = (int)(255.0*(v*(1.-s*(1.-f)))); 52 | v *= 255; 53 | i %= 6; 54 | int vi = (int)v; 55 | if (i == 0) { r = vi; g = t; b = p; } 56 | else if (i == 1) { r = q; g = vi; b = p; } 57 | else if (i == 2) { r = p; g = vi; b = t; } 58 | else if (i == 3) { r = p; g = q; b = vi; } 59 | else if (i == 4) { r = t; g = p; b = vi; } 60 | else if (i == 5) { r = vi; g = p; b = q; } 61 | } 62 | 63 | public: 64 | tqdm() { 65 | if (in_screen) { 66 | set_theme_basic(); 67 | color_transition = false; 68 | } else if (in_tmux) { 69 | color_transition = false; 70 | } 71 | } 72 | 73 | void reset() { 74 | t_first = std::chrono::system_clock::now(); 75 | t_old = std::chrono::system_clock::now(); 76 | n_old = 0; 77 | deq_t.clear(); 78 | deq_n.clear(); 79 | period = 1; 80 | nupdates = 0; 81 | total_ = 0; 82 | label = ""; 83 | } 84 | 85 | void set_theme_line() { bars = {"─", "─", "─", "╾", "╾", "╾", "╾", "━", "═"}; } 86 | void set_theme_circle() { bars = {" ", "◓", "◑", "◒", "◐", "◓", "◑", "◒", "#"}; } 87 | void set_theme_braille() { bars = {" ", "⡀", "⡄", "⡆", "⡇", "⡏", "⡟", "⡿", "⣿" }; } 88 | void set_theme_braille_spin() { bars = {" ", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠇", "⠿" }; } 89 | void set_theme_vertical() { bars = {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "█"}; } 90 | void set_theme_basic() { 91 | bars = {" ", " ", " ", " ", " ", " ", " ", " ", "#"}; 92 | right_pad = "|"; 93 | } 94 | void set_label(std::string label_) { label = label_; } 95 | void disable_colors() { 96 | color_transition = false; 97 | use_colors = false; 98 | } 99 | 100 | void finish() { 101 | progress(total_,total_); 102 | printf("\n"); 103 | fflush(stdout); 104 | } 105 | void progress(int curr, int tot) { 106 | if(is_tty && (curr%period == 0)) { 107 | total_ = tot; 108 | nupdates++; 109 | auto now = std::chrono::system_clock::now(); 110 | double dt = ((std::chrono::duration)(now - t_old)).count(); 111 | double dt_tot = ((std::chrono::duration)(now - t_first)).count(); 112 | int dn = curr - n_old; 113 | n_old = curr; 114 | t_old = now; 115 | if (deq_n.size() >= smoothing) deq_n.erase(deq_n.begin()); 116 | if (deq_t.size() >= smoothing) deq_t.erase(deq_t.begin()); 117 | deq_t.push_back(dt); 118 | deq_n.push_back(dn); 119 | 120 | double avgrate = 0.; 121 | if (use_ema) { 122 | avgrate = deq_n[0] / deq_t[0]; 123 | for (unsigned int i = 1; i < deq_t.size(); i++) { 124 | double r = 1.0*deq_n[i]/deq_t[i]; 125 | avgrate = alpha_ema*r + (1.0-alpha_ema)*avgrate; 126 | } 127 | } else { 128 | double dtsum = std::accumulate(deq_t.begin(),deq_t.end(),0.); 129 | int dnsum = std::accumulate(deq_n.begin(),deq_n.end(),0.); 130 | avgrate = dnsum/dtsum; 131 | } 132 | 133 | // learn an appropriate period length to avoid spamming stdout 134 | // and slowing down the loop, shoot for ~25Hz and smooth over 3 seconds 135 | if (nupdates > 10) { 136 | period = (int)( std::min(std::max((1.0/25)*curr/dt_tot,1.0), 5e5)); 137 | smoothing = 25*3; 138 | } 139 | double peta = (tot-curr)/avgrate; 140 | double pct = (double)curr/(tot*0.01); 141 | if( ( tot - curr ) <= period ) { 142 | pct = 100.0; 143 | avgrate = tot/dt_tot; 144 | curr = tot; 145 | peta = 0; 146 | } 147 | 148 | double fills = ((double)curr / tot * width); 149 | int ifills = (int)fills; 150 | 151 | printf("\015 "); 152 | if (use_colors) { 153 | if (color_transition) { 154 | // red (hue=0) to green (hue=1/3) 155 | int r = 255, g = 255, b = 255; 156 | hsv_to_rgb(0.0+0.01*pct/3,0.65,1.0, r,g,b); 157 | printf("\033[38;2;%d;%d;%dm ", r, g, b); 158 | } else { 159 | printf("\033[32m "); 160 | } 161 | } 162 | for (int i = 0; i < ifills; i++) std::cout << bars[8]; 163 | if (!in_screen and (curr != tot)) printf("%s",bars[(int)(8.0*(fills-ifills))]); 164 | for (int i = 0; i < width-ifills-1; i++) std::cout << bars[0]; 165 | printf("%s ", right_pad.c_str()); 166 | if (use_colors) printf("\033[1m\033[31m"); 167 | printf("%4.1f%% ", pct); 168 | if (use_colors) printf("\033[34m"); 169 | 170 | std::string unit = "Hz"; 171 | double div = 1.; 172 | if (avgrate > 1e6) { 173 | unit = "MHz"; div = 1.0e6; 174 | } else if (avgrate > 1e3) { 175 | unit = "kHz"; div = 1.0e3; 176 | } 177 | printf("[%4d/%4d | %3.1f %s | %.0fs<%.0fs] ", curr,tot, avgrate/div, unit.c_str(), dt_tot, peta); 178 | printf("%s ", label.c_str()); 179 | if (use_colors) printf("\033[0m\033[32m\033[0m\015 "); 180 | 181 | if( ( tot - curr ) > period ) fflush(stdout); 182 | 183 | } 184 | } 185 | }; 186 | #endif 187 | --------------------------------------------------------------------------------