gitee.com/quant1x/gox@v1.21.2/progressbar/bar.go (about) 1 package progressbar 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 ) 8 9 // Bar 进度条 10 type Bar struct { 11 mu sync.Mutex 12 line int 13 prefix string 14 total int 15 width int 16 bar1 string 17 bar2 string 18 advance chan struct{} // for data 19 done chan struct{} // for updateCost 20 finished chan struct{} // 等待结束信号 21 currents map[string]int 22 current int 23 before int 24 rate int 25 speed int 26 cost int 27 estimate int 28 fast int 29 slow int 30 srcUnit string 31 dstUnit string 32 change int 33 start time.Time 34 } 35 36 const ( 37 defaultFast = 20 38 defaultSlow = 5 39 defaultTickTimes = time.Millisecond * 500 // 默认定时刷新进度条的间隔时间 40 defaultSleepTimes = time.Millisecond * 100 // 默认sleep时间 41 ) 42 43 func NewBar(line int, prefix string, total int) *Bar { 44 if total <= 0 { 45 return nil 46 } 47 48 if line <= 0 { 49 gMaxLine++ 50 line = gMaxLine 51 } 52 if line > gMaxLine { 53 gMaxLine = line 54 } 55 56 bar := &Bar{ 57 line: line, 58 prefix: prefix, 59 total: total, 60 fast: defaultFast, 61 slow: defaultSlow, 62 width: 100, 63 advance: make(chan struct{}, total), 64 done: make(chan struct{}, total), 65 finished: make(chan struct{}), 66 currents: make(map[string]int), 67 change: 1, 68 start: time.Now(), 69 } 70 bar.initBar(bar.width) 71 go bar.updateCost() 72 go bar.run() 73 74 return bar 75 } 76 77 // SetPrefix 设置进度条前端文字 78 func (b *Bar) SetPrefix(prefix string) { 79 b.mu.Lock() 80 defer b.mu.Unlock() 81 b.prefix = prefix 82 } 83 84 func (b *Bar) initBar(width int) { 85 b.mu.Lock() 86 defer b.mu.Unlock() 87 for i := 0; i < width; i++ { 88 b.bar1 += "=" 89 b.bar2 += "-" 90 } 91 } 92 93 func (b *Bar) SetUnit(src string, dst string, change int) { 94 b.mu.Lock() 95 defer b.mu.Unlock() 96 b.srcUnit = src 97 b.dstUnit = dst 98 b.change = change 99 100 if b.change == 0 { 101 b.change = 1 102 } 103 } 104 105 func (b *Bar) SetSpeedSection(fast, slow int) { 106 b.mu.Lock() 107 defer b.mu.Unlock() 108 if fast > slow { 109 b.fast, b.slow = fast, slow 110 } else { 111 b.fast, b.slow = slow, fast 112 } 113 } 114 115 // Add 进度条前进, 同时也会触发其它信号 116 func (b *Bar) Add(n ...int) { 117 b.mu.Lock() 118 defer b.mu.Unlock() 119 step := 1 120 if len(n) > 0 { 121 step = n[0] 122 } 123 b.current += step 124 // 计算速度 125 b.computeAndUpdate() 126 // 进度条达到100%, 通知工作协程结束并关闭channel 127 if b.current >= b.total { 128 // 通知updateCost协程结束 129 b.done <- struct{}{} 130 close(b.done) 131 } 132 } 133 134 func (b *Bar) computeAndUpdate() { 135 now := time.Now() 136 nowKey := now.Format("20060102150405") 137 befKey := now.Add(time.Minute * -1).Format("20060102150405") 138 b.currents[nowKey] = b.current 139 if v, ok := b.currents[befKey]; ok { 140 b.before = v 141 } 142 delete(b.currents, befKey) 143 144 b.rate = b.current * 100 / b.total 145 b.cost = int(time.Since(b.start) / time.Second) 146 if b.cost == 0 { 147 b.speed = b.current * 100 148 } else if b.before == 0 { 149 b.speed = b.current * 100 / b.cost 150 } else { 151 b.speed = (b.current - b.before) * 100 / 60 152 } 153 154 if b.speed != 0 { 155 b.estimate = (b.total - b.current) * 100 / b.speed 156 } 157 b.advance <- struct{}{} // 发送更新进度条信号 158 } 159 160 // 发送bar更新信号 161 func (b *Bar) sendBarUpdateSignal() { 162 b.mu.Lock() 163 defer b.mu.Unlock() 164 // 统计数据 165 b.computeAndUpdate() 166 } 167 168 func (b *Bar) updateCost() { 169 for { 170 select { 171 case <-time.After(defaultTickTimes): 172 b.sendBarUpdateSignal() 173 case <-b.done: 174 // 补全不满100%进度的信号 175 for b.rate <= 100 { 176 b.sendBarUpdateSignal() 177 if b.rate >= 100 { 178 break 179 } 180 time.Sleep(defaultSleepTimes) 181 } 182 close(b.advance) 183 return 184 } 185 } 186 } 187 188 // Wait 等待进度条刷新run协程结束 189 func (b *Bar) Wait() { 190 <-b.finished 191 } 192 193 func (b *Bar) run() { 194 defer func() { 195 b.finished <- struct{}{} 196 }() 197 // 只有关闭channel才会结束循环, 且不能对channel加速 198 for range b.advance { 199 text := b.barMsg() 200 barPrintf(b.line, "\r%s", text) 201 } 202 } 203 204 // 重置进度条消息 205 func (b *Bar) barMsg() string { 206 b.mu.Lock() 207 defer b.mu.Unlock() 208 unit := "" 209 change := 1 210 if b.srcUnit != "" { 211 unit = b.srcUnit 212 } 213 214 if b.dstUnit != "" { 215 unit = b.dstUnit 216 change = b.change 217 } 218 219 prefix := fmt.Sprintf("%s", b.prefix) 220 rate := fmt.Sprintf("%3d%%", b.rate) 221 speed := fmt.Sprintf("%3.2f %s ps", 0.01*float64(b.speed)/float64(change), unit) 222 cost := b.timeFmt(b.cost) 223 estimate := b.timeFmt(b.estimate) 224 ct := fmt.Sprintf(" (%d/%d)", b.current, b.total) 225 barLen := b.width - len(prefix) - len(rate) - len(speed) - len(cost) - len(estimate) - len(ct) - 10 226 bar1Len := barLen * b.rate / 100 227 bar2Len := barLen - bar1Len 228 229 //realBar1 := bar1[:bar1Len] 230 realBar1 := b.bar1[:bar1Len] 231 var realBar2 string 232 if bar2Len > 0 { 233 //realBar2 = ">" + bar2[:bar2Len-1] 234 realBar2 = ">" + b.bar2[:bar2Len-1] 235 } 236 237 msg := fmt.Sprintf(`%s %s%s [%s%s] %s %s in: %s`, prefix, rate, ct, realBar1, realBar2, speed, cost, estimate) 238 switch { 239 case b.speed <= b.slow*100: 240 return "\033[0;31m" + msg + "\033[0m" 241 case b.speed > b.slow*100 && b.speed < b.fast*100: 242 return "\033[0;33m" + msg + "\033[0m" 243 default: 244 return "\033[0;32m" + msg + "\033[0m" 245 } 246 } 247 248 func (b *Bar) timeFmt(cost int) string { 249 var h, m, s int 250 h = cost / 3600 251 m = (cost - h*3600) / 60 252 s = cost - h*3600 - m*60 253 254 return fmt.Sprintf("%02d:%02d:%02d", h, m, s) 255 }