origin
Recently, the gwpca method for remote sensing image fusion is too slow when the bandwidth is relatively large. I need a progress bar to indicate it. Then I went to the library of the progress bar and found that the corresponding library of C / C + + on GitHub does not seem to be able to run under vs. I spent some time writing one.
effect
realization
There are several factors that need to be considered
- Percentage completed
- Execution speed
- Time of execution
- Time remaining
In addition, the introduction of progress bar can’t destroy the existing execution structure. It’s better to be similar to the tqdm Library of Pythonstart
, update
So for C language, we need a timer to redraw the progress bar regularly (it is impossible to redraw the progress bar every time it is updated). Therefore, the whole progress bar contains two classes, one is the progress bar class, and the other is the timer class. In addition, we need to consider the issue of thread safety.
// Progress.hpp
#pragma once
#include <ctime>
#include <chrono>
#include <iostream>
#include <iomanip>
#include "Timer.hpp"
using namespace std::chrono;
class ProgressBar
{
protected:
//The length of the progress bar (excluding the front suffix)
unsigned int ncols;
//Quantity completed
std::atomic<unsigned int> finishedNum;
//Last completed quantity
unsigned int lastNum;
//Total
unsigned int totalNum;
//Coefficient between progress bar length and percentage
double colsRatio;
//Start time
steady_clock::time_point beginTime;
//Last redraw time
steady_clock::time_point lastTime;
//Redraw cycle
milliseconds interval;
Timer timer;
public:
ProgressBar(unsigned int totalNum, milliseconds interval) : totalNum(totalNum), interval(interval), finishedNum(0), lastNum(0), ncols(80), colsRatio(0.8) {}
//Start
void start();
//Done
void finish();
//Update
void update() { return this->update(1); }
//Update multiple quantities at a time
void update(unsigned int num) { this->finishedNum += num; }
//Get the length of progress bar
unsigned int getCols() { return this->ncols; }
//Set progress bar length
void setCols(unsigned int ncols) { this->ncols = ncols; this->colsRatio = ncols / 100; }
//Redraw
void show();
};
void ProgressBar::start() {
//Record the start time and initialize the timer
this->beginTime = steady_clock::now();
this->lastTime = this->beginTime;
//The timer is used to call the redraw function regularly
this->timer.start(this->interval.count(), std::bind(&ProgressBar::show, this));
}
//Redraw函数
void ProgressBar::show() {
//Clear last drawn content
std::cout << "\r";
//Record the time point of redrawing
steady_clock::time_point now = steady_clock::now();
//Get completed quantity
unsigned int tmpFinished = this->finishedNum.load();
//Gets the time interval from the start time and the last redraw time
auto timeFromStart = now - this->beginTime;
auto timeFromLast = now - this->lastTime;
//The number of completed this time
unsigned int gap = tmpFinished - this->lastNum;
//Calculation speed
double rate = gap / duration<double>(timeFromLast).count();
//Percentage to be displayed
double present = (100.0 * tmpFinished) / this->totalNum;
//Print percentage
std::cout << std::setprecision(1) << std::fixed << present << "%|";
//Calculate how many = symbols should be drawn
int barWidth = present * this->colsRatio;
//Print completed and incomplete progress bars
std::cout << std::setw(barWidth) << std::setfill('=') << "=";
std::cout << std::setw(this->ncols - barWidth) << std::setfill(' ') << "|";
//Printing speed
std::cout << std::setprecision(1) << std::fixed << rate << "op/s|";
//The next two parts are printing time passed and remaining time
int timeFromStartCount = duration<double>(timeFromStart).count();
std::time_t tfs = timeFromStartCount;
tm tmfs;
gmtime_s(&tmfs, &tfs);
std::cout << std::put_time(&tmfs, "%X") << "|";
int timeLast;
if (rate != 0) {
//The remaining time is estimated by the speed of this time and the number of unfinished projects
timeLast = (this->totalNum - tmpFinished) / rate;
}
else {
timeLast = INT_MAX;
}
if ((this->totalNum - tmpFinished) == 0) {
timeLast = 0;
}
std::time_t tl = timeLast;
tm tml;
gmtime_s(&tml, &tl);
std::cout << std::put_time(&tml, "%X");
this->lastNum = tmpFinished;
this->lastTime = now;
}
void ProgressBar::finish() {
//Stop timer
this->timer.stop();
std::cout << std::endl;
}
// Timer.hpp
#pragma once
#include <functional>
#include <chrono>
#include <thread>
#include <atomic>
#include <memory>
#include <mutex>
#include <condition_variable>
using namespace std::chrono;
class Timer
{
public:
Timer() : _expired(true), _try_to_expire(false)
{}
Timer(const Timer& timer)
{
_expired = timer._expired.load();
_try_to_expire = timer._try_to_expire.load();
}
~Timer()
{
stop();
}
void start(int interval, std::function<void()> task)
{
// is started, do not start again
if (_expired == false)
return;
// start async timer, launch thread and wait in that thread
_expired = false;
std::thread([this, interval, task]() {
while (!_try_to_expire)
{
// sleep every interval and do the task again and again until times up
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
task();
}
{
// timer be stopped, update the condition variable expired and wake main thread
std::lock_guard<std::mutex> locker(_mutex);
_expired = true;
_expired_cond.notify_one();
}
}).detach();
}
void startOnce(int delay, std::function<void()> task)
{
std::thread([delay, task]() {
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
task();
}).detach();
}
void stop()
{
// do not stop again
if (_expired)
return;
if (_try_to_expire)
return;
// wait until timer
_try_to_expire = true; // change this bool value to make timer while loop stop
{
std::unique_lock<std::mutex> locker(_mutex);
_expired_cond.wait(locker, [this] {return _expired == true; });
// reset the timer
if (_expired == true)
_try_to_expire = false;
}
}
private:
std::atomic<bool> _expired; // timer stopped status
std::atomic<bool> _try_to_expire; // timer is in stop process
std::mutex _mutex;
std::condition_variable _expired_cond;
};
Timer class is a direct copy of an article
Functions that can be added
Readers can adjust the structure by themselves and add some interesting small functions. For example, the symbol used to express the completed content can be replaced with a favorite symbol, or a color can be added
There are also some complex functions, such as grouping progress bar, etc. However, because I don’t have this requirement, I haven’t studied it. Readers can study it by themselves
So far, this article about using C + + to implement a command-line progress bar example code is introduced here. For more related C + + command-line progress bar content, please search previous articles of developer or continue to browse the following related articles. I hope you can support developer more in the future!