golang进度条

进度条元素

▪ 总量 ▪ 当前进度 ▪ 耗时

通过以上元素可以延伸出:完成百分比、速度、预计剩余时间、根据设置速度快慢阈值用不同的颜色来显示进度条。

实现

进度条

type Bar struct {
    mu       sync.Mutex
    line     int                   //显示在哪行  多进度条的时候用
    prefix   string                //进度条前置描述
    total    int                   //总量
    width    int                   //宽度
    advance  chan bool             //是否刷新进度条
    done     chan bool             //是否完成
    currents map[string]int        //一段时间内每个时间点的完成量
    current  int                   //当前完成量
    rate     int                   //进度百分比
    speed    int                   //速度
    cost     int                   //耗时
    estimate int                   //预计剩余完成时间
    fast     int                   //速度快的阈值
    slow     int                   //速度慢的阈值
}

细节控制

耗时

一个计时器,需要注意的是即使进度没有变化,耗时也是递增的,看过一个多进程进度条的写法,没有注意这块,一个goroutine:

func (b *Bar) updateCost() {
    for {
        select {
        case <-time.After(time.Second):
            b.cost++
            b.advance <- true
        case <-b.done:
            return
        }
    }
}

进度

通过Add方法来递增当前完成的量,然后计算相关的值:速度、百分比、剩余完成时间等,这里计算速度一般是取最近一段时间内的平均速度,如果是全部的完成量直接除当前耗时的话计算出来的速度并不准确,同时会影响剩余时间的估计。

func (b *Bar) Add() {
    b.mu.Lock()
    now := time.Now()
    nowKey := now.Format("20060102150405")
    befKey := now.Add(time.Minute * -1).Format("20060102150405")
    b.current++
    b.currents[nowKey] = b.current
    if v, ok := b.currents[befKey]; ok {
        b.before = v
    }
    delete(b.currents, befKey)
    lastRate := b.rate
    lastSpeed := b.speed
    b.rate = b.current * 100 / b.total
    if b.cost == 0 {
        b.speed = b.current * 100
    } else if b.before == 0 {
        b.speed = b.current * 100 / b.cost
    } else {
        b.speed = (b.current - b.before) * 100 / 60
    }

    if b.speed != 0 {
        b.estimate = (b.total - b.current) * 100 / b.speed
    }
    b.mu.Unlock()
    if lastRate != b.rate || lastSpeed != b.speed {
        b.advance <- true
    }

    if b.rate >= 100 {
        close(b.done)
        close(b.advance)
    }
}

显示

才用最简单的\r, 以及多进度条同时展示的话需要用到终端光标移动,这里只需要用到光标的上下移动即可,\033[nA 向上移动n行,\033[nB 向下移动n行。

移动到第n行:

func move(line int) {
    fmt.Printf("\033[%dA\033[%dB", gCurrentLine, line)
    gCurrentLine = line
}

为了支持其他的标准输出不影响进度条的展示,还需要提供Print, Printf, Println 的方法, 用于计算当前光标所在位置,每个进度条都会有自己的所在行,显示的时候光标需要移动到对应的行。

源码

https://github.com/qianlnk/pgbar

效果


原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2018-01-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端小叙

react给一个div行内加背景图片并实现cover覆盖模式居中显示

这里注意,如果简写里要写background-size,则这里必须写 /  ,否则整个背景图片样式没有解析出来。

73220
来自专栏无所事事者爱嘲笑

Vue中scoped css和css module比较

50320
来自专栏Youngxj

给你的博客文章加入”展开/收缩功能‘’

29620
来自专栏听雨堂

CSS实现按钮的两张图片的同步出现

      通过图片交换实现了按钮图片,但是存在一个问题,老是先加载一个图片,另外一个却加载得慢,效果反而不好。这是网上的解决方法,实现css图片的预加载: <...

21590
来自专栏Python Web学习记录及整理

HTML学习记录及整理

<!DOCTYPE ”此文仅为个人学习的知识梳理,权威且更详细的内容请查阅w3school。“>

50680
来自专栏禁心尽力

快速完成(图片旋转,查看原图)

一个小小的前端需求送给大家,使用js实现图片旋转,并且点击图片能够实现规定格式的大图。   主要使用的是jQuery的delegate()方法实现图片旋转,该...

38280
来自专栏破晓之歌

AI-icon制作 原

9050
来自专栏性能与架构

体验JPEG图片渐进模式

前几天看了一个高手朋友的文章后才知道,原来JPEG文件有两种保存方式,分别是Baseline JPEG(标准型)和Progressive JPEG(渐进式) ...

45180
来自专栏IT开发技术与工作效率

IntelliJ IDEA 教程 技巧篇本篇概要

13320
来自专栏数据小魔方

单元格格式/条件格式的妙用

今天跟大家分享一种常用的技巧——利用单元格格式/条件格式按颜色划分不同数据等级。 ▽▼▽ 这个技巧在绩效管理、成绩考评以及其他设计考核的工作中都会经常用到。首...

62070

扫码关注云+社区

领取腾讯云代金券