鱼C论坛

 找回密码
 立即注册
查看: 1263|回复: 0

[技术交流] 一个用于在控制台中实现进度显示的类

[复制链接]
发表于 2020-9-10 16:06:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
由于在控制台中只能以打印字符的形式来进行输出,很多时候我们在运行比较大的程序时无法知道完成进度,所以写了一个类来实现这一功能。
//DosProgressBar header file
//"DosProgressBar"用于在控制台中实现进度显示
//版本号: 1.0.1
//作者:三点一洲

#pragma once
#include <cstring>
#include <cstdio>
#include <string>
#include <iostream>
#include <windows.h>
#define __ARR_CAPACITY 100
#define _INIT_VALUE 0
#define _MAX_VALUE 1
#define _PERCENT_MULTI 100
#define _BLOCK_NUMBERS 32
#define _DEFAULT_FLUSH_FREQUENCY 65536
#define _FLUSH_FREQUENCY 3
#define _FILL_BLOCK "█"
#define _STANDEARD_OUT_SIZE (_BLOCK_NUMBERS * 2 + 2)
#define _UNUSED_BAR 0
#define _USING_BAR 1
#define _USED_BAR 2

//设置光标位置(x, y)
void gotoxy(short, short);
//设置光标位置(COORD)
void gotoxy(COORD);
//返回当前光标位置
COORD wherexy();
//获取光标的位置y
int wherey();
//获取光标的位置x
int wherex();
//计算合适的刷新频率
int calcFrequency(int);

class DosProgressBar
{
    typedef int bar_status;
    typedef double progress_value;

private:
    progress_value nowValue;
    bar_status status;
    COORD outPos;
    int flushCount;
    int flushFrequency;

public:
    //格式化进度
    inline std::string percentFormate()
    /* 格式化当前进度 */
    {
        char s[__ARR_CAPACITY];
        sprintf(s, "%.2f%%", nowValue * _PERCENT_MULTI);
        //std::cout<<std::string(s)+"\n";
        return std::string(s);
    }
    //设置当前进度
    inline progress_value setNowValue(progress_value pv)
    /* 设置进度值 */
    {
        nowValue = pv;
        return pv;
    }
    //获得当前进度
    inline progress_value getNowValue()
    /* 获得当前进度 */
    {
        return nowValue;
    }

private:
    //int printProgressBar(const char end = '\0');
    /* 按频率打印出进度条 */
    //int printProgressBar(const std::string &, const char end = '\0');
    /* 按频率打印出有后缀进度条 */

    //回退到开始光标
    void backToStartPos();
    //立即打印进度条
    int printProgressBarSoon(const char end = '\0');
    //立即打印有后缀进度条
    int printProgressBarSoon(const std::string &, const char end = '\0');

public:
    //刷新进度条
    void flushProgressBar(progress_value);
    //重置进度条
    void reSetBar();
    //设置合理刷新频率
    void setFrequency(int);

public:
    inline DosProgressBar(void) : nowValue(0), flushCount(0), outPos({0, 0}), status(_UNUSED_BAR), flushFrequency(_DEFAULT_FLUSH_FREQUENCY)
    {
    }
    inline DosProgressBar(progress_value _nowValue) : nowValue(_nowValue), flushCount(0), outPos({0, 0}), status(_UNUSED_BAR), flushFrequency(_DEFAULT_FLUSH_FREQUENCY)
    {
    }
    ~DosProgressBar(void) {}
};
//DosProgressBar source file
//"DosProgressBar"用于在控制台中实现进度显示
//版本号: 1.0.1
//作者:三点一洲

#include "DosProgressBar.h"
#include <cmath>

//获取光标的位置x
int wherex()
{
    CONSOLE_SCREEN_BUFFER_INFO pBuffer;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);
    return pBuffer.dwCursorPosition.X;
}

//获取光标的位置y
int wherey()
{
    CONSOLE_SCREEN_BUFFER_INFO pBuffer;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);
    return pBuffer.dwCursorPosition.Y;
}

//返回当前光标位置
COORD wherexy()
{
    COORD _coord;
    CONSOLE_SCREEN_BUFFER_INFO pBuffer;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);
    _coord.X = pBuffer.dwCursorPosition.X;
    _coord.Y = pBuffer.dwCursorPosition.Y;
    return _coord;
}

//设置光标位置(x, y)
void gotoxy(short x, short y)
{
    COORD pos = {x, y};
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 
    SetConsoleCursorPosition(hOut, pos);        
}

//设置光标位置(COORD)
void gotoxy(COORD _coord)
{
    COORD pos = _coord;
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 
    SetConsoleCursorPosition(hOut, pos);      
}

//计算合适的刷新频率
int calcFrequency(int _t)
{
    int t = (int)(_t / _BLOCK_NUMBERS / ((int)(log( _t < exp(2) ? exp(2) : _t ) / 2)) + 1);
    return t;
}

//回退到开始光标
void DosProgressBar::backToStartPos()
{
    gotoxy(outPos);
    return;
}

//立即打印进度条
int DosProgressBar::printProgressBarSoon(const char end)
{
    std::string outs("|");
    int block = (int)(nowValue * _BLOCK_NUMBERS);
    for (int i = 0; i < block; i++)
    {
        outs += _FILL_BLOCK;
    }
    outs += std::string(_STANDEARD_OUT_SIZE - 2 - block * 2, ' ');
    outs += "|";
    outs += "  " + percentFormate();
    if (end != '\0')
        outs += end;
    return printf("%s", outs.c_str());
}

//立即打印有后缀进度条
int DosProgressBar::printProgressBarSoon(const std::string &suffix, const char end)
{
    int n = printProgressBarSoon('\0') + printf("%s", suffix.c_str());
    if (end != '\0')
        return n + printf("%c", end);
    else
        return n;
}

//刷新进度条
void DosProgressBar::flushProgressBar(progress_value pv)
{
    if (status == _UNUSED_BAR)
    {
        //std::cout<<"\n"<<flushFrequency<<std::endl;
        status = _USING_BAR;
        COORD _co = wherexy();
        if (_co.X > 0)
        {
            _co.Y++;
            _co.X = 0;
        }
        outPos = _co;
        gotoxy(outPos);
    }
    else if (status == _USED_BAR)
    {
        std::cerr << "进度条已使用,请重置后继续使用" << std::endl;
        //exception 1
        //exit(0);
    }
    setNowValue(pv);
    ++flushCount;
    int resetFlag = flushCount / flushFrequency;
    flushCount %= flushFrequency;
    if (resetFlag || nowValue == 1.0)
    {
        gotoxy(outPos);
        printProgressBarSoon('\n');
        if (!(nowValue == 1.0))
        {
            gotoxy(outPos);
        }
        else
        {
            status = _USED_BAR;
        }
    }
}

//重置进度条
void DosProgressBar::reSetBar()
{
}

//设置合理刷新频率
void DosProgressBar::setFrequency(int total)
{
    flushFrequency = calcFrequency(total);
    return;
}
#include "DosProgressBar.h"
#define N 100000000  //总循环次数
int main()
{
    DosProgressBar dpb(0.0);
    dpb.setFrequency(N);
    for (int i = 0; i <= N ; i++)
    {
        dpb.flushProgressBar(i * 1.0 / N);
    }
    return 0;
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-13 03:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表