用C++實(shí)現(xiàn)一個(gè)命令行進(jìn)度條的示例代碼
緣起
最近做遙感影像融合的GWPCA方法,在帶寬比較大的時(shí)候速度太慢了,需要有個(gè)進(jìn)度條指示一下,然后我去找進(jìn)度條的庫,發(fā)現(xiàn)github上面的C/C++的相應(yīng)的庫似乎沒有能在VS下跑的,自己花了點(diǎn)時(shí)間寫了一個(gè)。
效果
實(shí)現(xiàn)
大概需要考慮這樣幾個(gè)要素
- 已完成的百分比
- 執(zhí)行速度
- 已執(zhí)行的時(shí)間
- 剩余時(shí)間
另外進(jìn)度條的引入不能破壞已有的執(zhí)行結(jié)構(gòu),最好和Python的tqdm庫類似,通過 start
, update
等函數(shù)來完成整個(gè)進(jìn)度條,因此對于C語言來說,需要一個(gè)定時(shí)器,定期將進(jìn)度條進(jìn)行重繪(不可能更新一次就重繪一次),因此整個(gè)進(jìn)度條就包含了兩個(gè)類,一個(gè)是進(jìn)度條類,一個(gè)是定時(shí)器類。另外需要考慮線程安全的問題。
// Progress.hpp #pragma once #include <ctime> #include <chrono> #include <iostream> #include <iomanip> #include "Timer.hpp" using namespace std::chrono; class ProgressBar { protected: // 進(jìn)度條的長度(不包含前后綴) unsigned int ncols; // 已完成的數(shù)量 std::atomic<unsigned int> finishedNum; // 上次的已完成數(shù)量 unsigned int lastNum; // 總數(shù) unsigned int totalNum; // 進(jìn)度條長度與百分比之間的系數(shù) double colsRatio; // 開始時(shí)間 steady_clock::time_point beginTime; // 上次重繪的時(shí)間 steady_clock::time_point lastTime; // 重繪周期 milliseconds interval; Timer timer; public: ProgressBar(unsigned int totalNum, milliseconds interval) : totalNum(totalNum), interval(interval), finishedNum(0), lastNum(0), ncols(80), colsRatio(0.8) {} // 開始 void start(); // 完成 void finish(); // 更新 void update() { return this->update(1); } // 一次更新多個(gè)數(shù)量 void update(unsigned int num) { this->finishedNum += num; } // 獲取進(jìn)度條長度 unsigned int getCols() { return this->ncols; } // 設(shè)置進(jìn)度條長度 void setCols(unsigned int ncols) { this->ncols = ncols; this->colsRatio = ncols / 100; } // 重繪 void show(); }; void ProgressBar::start() { // 記錄開始時(shí)間,并初始化定時(shí)器 this->beginTime = steady_clock::now(); this->lastTime = this->beginTime; // 定時(shí)器用于定時(shí)調(diào)用重繪函數(shù) this->timer.start(this->interval.count(), std::bind(&ProgressBar::show, this)); } // 重繪函數(shù) void ProgressBar::show() { // 清除上次的繪制內(nèi)容 std::cout << "\r"; // 記錄重繪的時(shí)間點(diǎn) steady_clock::time_point now = steady_clock::now(); // 獲取已完成的數(shù)量 unsigned int tmpFinished = this->finishedNum.load(); // 獲取與開始時(shí)間和上次重繪時(shí)間的時(shí)間間隔 auto timeFromStart = now - this->beginTime; auto timeFromLast = now - this->lastTime; // 這次完成的數(shù)量 unsigned int gap = tmpFinished - this->lastNum; // 計(jì)算速度 double rate = gap / duration<double>(timeFromLast).count(); // 應(yīng)顯示的百分?jǐn)?shù) double present = (100.0 * tmpFinished) / this->totalNum; // 打印百分?jǐn)?shù) std::cout << std::setprecision(1) << std::fixed << present << "%|"; // 計(jì)算應(yīng)該繪制多少=符號 int barWidth = present * this->colsRatio; // 打印已完成和未完成進(jìn)度條 std::cout << std::setw(barWidth) << std::setfill('=') << "="; std::cout << std::setw(this->ncols - barWidth) << std::setfill(' ') << "|"; // 打印速度 std::cout << std::setprecision(1) << std::fixed << rate << "op/s|"; // 之后的兩部分內(nèi)容分別為打印已過的時(shí)間和剩余時(shí)間 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) { // 剩余時(shí)間的估計(jì)是用這次的速度和未完成的數(shù)量進(jìn)行估計(jì) 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() { // 停止定時(shí)器 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; };
定時(shí)器類是直接copy了一篇 文章
可以增加的功能
讀者可以自行調(diào)整一下結(jié)構(gòu),增加一些有意思的小功能,比如說用于表示完成內(nèi)容的符號可以替換成大家喜歡的符號,或者加個(gè)顏色什么的
還有一些復(fù)雜的功能,比如說分組進(jìn)度條等,不過這個(gè)由于我沒這方面的需求,因此就沒研究了,讀者可以自行研究
到此這篇關(guān)于用C++實(shí)現(xiàn)一個(gè)命令行進(jìn)度條的示例代碼的文章就介紹到這了,更多相關(guān)C++ 命令行進(jìn)度條內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++11中條件標(biāo)量和互斥鎖應(yīng)用出現(xiàn)死鎖問題
這篇文章主要介紹了C++11中條件標(biāo)量和互斥鎖應(yīng)用出現(xiàn)死鎖思考,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06C++實(shí)現(xiàn)LeetCode(137.單獨(dú)的數(shù)字之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(137.單獨(dú)的數(shù)字之二),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C++機(jī)房預(yù)約系統(tǒng)實(shí)現(xiàn)流程實(shí)例
這篇文章主要介紹了C++機(jī)房預(yù)約系統(tǒng)實(shí)現(xiàn)流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-10-10c語言實(shí)現(xiàn)二叉查找樹實(shí)例方法
這篇文章主要介紹了一個(gè)c語言版的二叉查找樹實(shí)現(xiàn),二叉查找樹,支持的操作包括:SERACH、MINIMUM、MAXIMUM、PREDECESSOR、SUCCESSOR、INSERT、DELETE,大家參考使用吧2013-11-11基于Matlab實(shí)現(xiàn)嗅覺優(yōu)化算法的示例代碼
嗅覺劑優(yōu)化是一種新穎的優(yōu)化算法,旨在模仿氣味分子源尾隨的藥劑的智能行為。本文將利用Matlab實(shí)現(xiàn)這一智能優(yōu)化算法,需要的可以參考一下2022-05-05