Codebase list golang-github-vbauerster-mpb / ca88750
Breaking change: decor package refactoring Vladimir Bauer 8 years ago
21 changed file(s) with 492 addition(s) and 409 deletion(s). Raw diff Collapse all Expand all
88 "time"
99 "unicode/utf8"
1010
11 "github.com/VividCortex/ewma"
1112 "github.com/vbauerster/mpb/decor"
1213 )
1314
2122
2223 const (
2324 formatLen = 5
24 etaAlpha = 0.12
2525 )
2626
2727 type barRunes [formatLen]rune
3535 cacheState *bState
3636 operateState chan func(*bState)
3737 frameReaderCh chan io.Reader
38 startBlockCh <-chan time.Time
3839
3940 // done is closed by Bar's goroutine, after cacheState is written
4041 done chan struct{}
4748 id int
4849 width int
4950 runes barRunes
50 etaAlpha float64
5151 total int64
5252 current int64
5353 totalAutoIncrTrigger int64
6262 startTime time.Time
6363 blockStartTime time.Time
6464 timeElapsed time.Duration
65 timePerItemEstimate time.Duration
66 timeRemaining time.Duration
67 aDecorators []decor.DecoratorFunc
68 pDecorators []decor.DecoratorFunc
65 aDecorators []decor.Decorator
66 pDecorators []decor.Decorator
6967 refill *refill
7068 bufP, bufB, bufA *bytes.Buffer
7169 panicMsg string
7270
71 ewmAverage ewma.MovingAverage
72
7373 // following options are assigned to the *Bar
74 priority int
75 runningBar *Bar
74 priority int
75 runningBar *Bar
76 startBlockCh chan time.Time
7677 }
7778 refill struct {
7879 char rune
9596 id: id,
9697 priority: id,
9798 total: total,
98 etaAlpha: etaAlpha,
9999 dynamic: dynamic,
100100 }
101101
112112 b := &Bar{
113113 priority: s.priority,
114114 runningBar: s.runningBar,
115 startBlockCh: s.startBlockCh,
115116 operateState: make(chan func(*bState)),
116117 frameReaderCh: make(chan io.Reader, 1),
117118 done: make(chan struct{}),
118119 shutdown: make(chan struct{}),
119120 }
120121
122 s.startBlockCh = nil
123
121124 if b.runningBar != nil {
122125 b.priority = b.runningBar.priority
123126 }
143146 }
144147
145148 // ProxyReader wrapper for io operations, like io.Copy
146 func (b *Bar) ProxyReader(r io.Reader) *Reader {
147 return &Reader{r, b}
149 func (b *Bar) ProxyReader(r io.Reader, startBlock ...chan<- time.Time) *Reader {
150 proxyReader := &Reader{
151 Reader: r,
152 bar: b,
153 }
154 if len(startBlock) > 0 {
155 proxyReader.startBlockCh = startBlock[0]
156 }
157 return proxyReader
148158 }
149159
150160 // Increment is a shorthand for b.IncrBy(1)
222232 // SetTotal sets total dynamically. The final param indicates the very last set,
223233 // in other words you should set it to true when total is determined.
224234 func (b *Bar) SetTotal(total int64, final bool) {
225 select {
226 case b.operateState <- func(s *bState) {
235 b.operateState <- func(s *bState) {
227236 if total != 0 {
228237 s.total = total
229238 }
230239 s.dynamic = !final
231 }:
232 case <-b.done:
233 }
234 }
235
236 // StartBlock updates start timestamp of the current increment block.
237 // It is optional to call, unless ETA decorator is used.
238 // If *bar.ProxyReader is used, it will be called implicitly.
239 func (b *Bar) StartBlock() {
240 now := time.Now()
241 select {
242 case b.operateState <- func(s *bState) {
243 if s.current == 0 {
244 s.startTime = now
245 }
246 s.blockStartTime = now
247 }:
248 case <-b.done:
249240 }
250241 }
251242
256247 case b.operateState <- func(s *bState) {
257248 s.current += int64(n)
258249 s.timeElapsed = now.Sub(s.startTime)
259 s.timeRemaining = s.calcETA(n, now.Sub(s.blockStartTime))
250 if s.ewmAverage != nil {
251 lastBlockTime := now.Sub(s.blockStartTime)
252 lastItemEstimate := float64(lastBlockTime) / float64(n)
253 s.ewmAverage.Add(lastItemEstimate)
254 }
260255 if s.dynamic {
261256 curp := decor.CalcPercentage(s.total, s.current, 100)
262257 if 100-curp <= s.totalAutoIncrTrigger {
285280 func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
286281 defer wg.Done()
287282 s.startTime = time.Now()
288 s.blockStartTime = s.startTime
289283 for {
290284 select {
291285 case op := <-b.operateState:
292286 op(s)
287 case now := <-b.startBlockCh:
288 s.blockStartTime = now
293289 case <-cancel:
294290 s.toComplete = true
295291 cancel = nil
348344 stat := newStatistics(s)
349345
350346 // render prepend functions to the left of the bar
351 for i, f := range s.pDecorators {
352 s.bufP.WriteString(f(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i]))
353 }
354
355 for i, f := range s.aDecorators {
356 s.bufA.WriteString(f(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i]))
347 for i, d := range s.pDecorators {
348 s.bufP.WriteString(d.Decor(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i]))
349 }
350
351 for i, d := range s.aDecorators {
352 s.bufA.WriteString(d.Decor(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i]))
357353 }
358354
359355 prependCount := utf8.RuneCount(s.bufP.Bytes())
429425 }
430426 }
431427
432 func (s *bState) calcETA(n int, lastBlockTime time.Duration) time.Duration {
433 lastItemEstimate := float64(lastBlockTime) / float64(n)
434 s.timePerItemEstimate = time.Duration((s.etaAlpha * lastItemEstimate) + (1-s.etaAlpha)*float64(s.timePerItemEstimate))
435 return time.Duration(s.total-s.current) * s.timePerItemEstimate
436 }
437
438428 func newStatistics(s *bState) *decor.Statistics {
439429 return &decor.Statistics{
440 ID: s.id,
441 Completed: s.completeFlushed,
442 Total: s.total,
443 Current: s.current,
444 StartTime: s.startTime,
445 TimeElapsed: s.timeElapsed,
446 TimeRemaining: s.timeRemaining,
447 TimePerItemEstimate: s.timePerItemEstimate,
430 ID: s.id,
431 Completed: s.completeFlushed,
432 Total: s.total,
433 Current: s.current,
434 StartTime: s.startTime,
435 TimeElapsed: s.timeElapsed,
448436 }
449437 }
450438
88 type BarOption func(*bState)
99
1010 // AppendDecorators let you inject decorators to the bar's right side
11 func AppendDecorators(appenders ...decor.DecoratorFunc) BarOption {
11 func AppendDecorators(appenders ...decor.Decorator) BarOption {
1212 return func(s *bState) {
13 s.aDecorators = append(s.aDecorators, appenders...)
13 for _, decorator := range appenders {
14 if t, ok := decorator.(*decor.EwmaETA); ok {
15 s.ewmAverage = t
16 s.startBlockCh = t.StartBlockCh
17 }
18 s.aDecorators = append(s.aDecorators, decorator)
19 }
1420 }
1521 }
1622
1723 // PrependDecorators let you inject decorators to the bar's left side
18 func PrependDecorators(prependers ...decor.DecoratorFunc) BarOption {
24 func PrependDecorators(prependers ...decor.Decorator) BarOption {
1925 return func(s *bState) {
20 s.pDecorators = append(s.pDecorators, prependers...)
26 for _, decorator := range prependers {
27 if t, ok := decorator.(*decor.EwmaETA); ok {
28 s.ewmAverage = t
29 s.startBlockCh = t.StartBlockCh
30 }
31 s.pDecorators = append(s.pDecorators, decorator)
32 }
2133 }
2234 }
2335
4759 func BarID(id int) BarOption {
4860 return func(s *bState) {
4961 s.id = id
50 }
51 }
52
53 // BarEtaAlpha option is a way to adjust ETA behavior.
54 // You can play with it, if you're not satisfied with default behavior.
55 // Default value is 0.12
56 func BarEtaAlpha(a float64) BarOption {
57 return func(s *bState) {
58 s.etaAlpha = a
5962 }
6063 }
6164
8989 total := 100
9090
9191 bar := p.AddBar(int64(total), PrependDecorators(
92 func(s *decor.Statistics, _ chan<- int, _ <-chan int) string {
92 decor.DecoratorFunc(func(s *decor.Statistics, _ chan<- int, _ <-chan int) string {
9393 if s.Current >= 42 {
9494 panic(wantPanic)
9595 }
9696 return "test"
97 },
97 }),
9898 ))
9999
100100 go func() {
1212 }
1313 }
1414
15 func BenchmarkIncrSingleBarStartBlock(b *testing.B) {
16 p := New(WithOutput(ioutil.Discard))
17 bar := p.AddBar(int64(b.N))
18 for i := 0; i < b.N; i++ {
19 bar.StartBlock()
20 bar.Increment()
21 }
22 }
23
2415 func BenchmarkIncrSingleBarWhileIsNotCompleted(b *testing.B) {
2516 p := New(WithOutput(ioutil.Discard))
2617 bar := p.AddBar(int64(b.N))
44 "math"
55 "time"
66 "unicode/utf8"
7
8 "github.com/VividCortex/ewma"
79 )
810
911 const (
1214 // | foo| b| Without DidentRight
1315 DidentRight = 1 << iota
1416
15 // DwidthSync bit enables same column width synchronization.
16 // Effective on multiple bars only.
17 DwidthSync
18
19 // DextraSpace bit adds extra space, makes sense with DwidthSync only.
17 // DextraSpace bit adds extra space, makes sense with DSyncWidth only.
2018 // When DidentRight bit set, the space will be added to the right,
2119 // otherwise to the left.
2220 DextraSpace
2321
24 // DSyncSpace is shortcut for DwidthSync|DextraSpace
25 DSyncSpace = DwidthSync | DextraSpace
26
27 // DSyncSpaceR is shortcut for DwidthSync|DextraSpace|DidentRight
28 DSyncSpaceR = DwidthSync | DextraSpace | DidentRight
22 // DSyncWidth bit enables same column width synchronization.
23 // Effective with multiple bars only.
24 DSyncWidth
25
26 // DSyncWidthR is shortcut for DSyncWidth|DidentRight
27 DSyncWidthR = DSyncWidth | DidentRight
28
29 // DSyncSpace is shortcut for DSyncWidth|DextraSpace
30 DSyncSpace = DSyncWidth | DextraSpace
31
32 // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DidentRight
33 DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
34 )
35
36 const (
37 ET_STYLE_GO = iota
38 ET_STYLE_HHMMSS
39 ET_STYLE_HHMM
40 ET_STYLE_MMSS
2941 )
3042
3143 // Statistics contains values useful for implementing a DecoratorFunc.
3244 type Statistics struct {
33 ID int
34 Completed bool
35 Total int64
36 Current int64
37 StartTime time.Time
38 TimeElapsed time.Duration
39 TimeRemaining time.Duration
40 TimePerItemEstimate time.Duration
41 }
42
43 // DecoratorFunc is a function that can be prepended and appended to the progress bar
45 ID int
46 Completed bool
47 Total int64
48 Current int64
49 StartTime time.Time
50 TimeElapsed time.Duration
51 }
52
53 type Decorator interface {
54 Decor(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string
55 }
56
57 type CompleteMessenger interface {
58 OnComplete(string, ...WC)
59 }
60
61 // DecoratorFunc is an adapter for Decorator interface
4462 type DecoratorFunc func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string
4563
46 // OnComplete returns decorator, which wraps provided `fn` decorator, with sole
47 // purpose to display final on complete message.
48 //
49 // `fn` DecoratorFunc to wrap
50 //
51 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
52 //
53 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
54 func OnComplete(fn DecoratorFunc, message string, width, conf int) DecoratorFunc {
55 msgDecorator := StaticName(message, width, conf)
56 return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
64 func (f DecoratorFunc) Decor(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
65 return f(s, widthAccumulator, widthDistributor)
66 }
67
68 // WC is a struct with two public fields W and C, both of int type.
69 // W represents width and C represents bit set of width related config.
70 type WC struct {
71 W int
72 C int
73 format string
74 }
75
76 func (wc WC) formatMsg(msg string, widthAccumulator chan<- int, widthDistributor <-chan int) string {
77 format := wc.buildFormat()
78 if (wc.C & DSyncWidth) != 0 {
79 widthAccumulator <- utf8.RuneCountInString(msg)
80 max := <-widthDistributor
81 if max == 0 {
82 max = wc.W
83 }
84 if (wc.C & DextraSpace) != 0 {
85 max++
86 }
87 return fmt.Sprintf(fmt.Sprintf(format, max), msg)
88 }
89 return fmt.Sprintf(fmt.Sprintf(format, wc.W), msg)
90 }
91
92 func (wc *WC) buildFormat() string {
93 if wc.format != "" {
94 return wc.format
95 }
96 wc.format = "%%"
97 if (wc.C & DidentRight) != 0 {
98 wc.format += "-"
99 }
100 wc.format += "%ds"
101 return wc.format
102 }
103
104 // Global convenience shortcuts
105 var (
106 WCSyncWidth = WC{C: DSyncWidth}
107 WCSyncWidthR = WC{C: DSyncWidthR}
108 WCSyncSpace = WC{C: DSyncSpace}
109 WCSyncSpaceR = WC{C: DSyncSpaceR}
110 )
111
112 // OnComplete returns decorator, which wraps provided decorator, with sole
113 // purpose to display provided message on complete event.
114 //
115 // `decorator` Decorator to wrap
116 //
117 // `message` message to display on complete event
118 //
119 // `wc` optional WC config
120 func OnComplete(decorator Decorator, message string, wc ...WC) Decorator {
121 if cm, ok := decorator.(CompleteMessenger); ok {
122 cm.OnComplete(message, wc...)
123 return decorator
124 }
125 msgDecorator := Name(message, wc...)
126 return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
57127 if s.Completed {
58 return msgDecorator(s, widthAccumulator, widthDistributor)
59 }
60 return fn(s, widthAccumulator, widthDistributor)
61 }
62 }
63
64 // StaticName returns static name/message decorator.
128 return msgDecorator.Decor(s, widthAccumulator, widthDistributor)
129 }
130 return decorator.Decor(s, widthAccumulator, widthDistributor)
131 })
132 }
133
134 // StaticName returns name decorator.
65135 //
66136 // `name` string to display
67137 //
68 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
69 //
70 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
71 func StaticName(name string, width, conf int) DecoratorFunc {
72 nameFn := func(*Statistics) string {
73 return name
74 }
75 return DynamicName(nameFn, width, conf)
76 }
77
78 // DynamicName returns dynamic name/message decorator.
79 //
80 // `messageFn` callback function to get dynamic string message
81 //
82 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
83 //
84 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
85 func DynamicName(messageFn func(*Statistics) string, width, conf int) DecoratorFunc {
86 format := "%%"
87 if (conf & DidentRight) != 0 {
88 format += "-"
89 }
90 format += "%ds"
91 return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
92 name := messageFn(s)
93 if (conf & DwidthSync) != 0 {
94 widthAccumulator <- utf8.RuneCountInString(name)
95 max := <-widthDistributor
96 if (conf & DextraSpace) != 0 {
97 max++
98 }
99 return fmt.Sprintf(fmt.Sprintf(format, max), name)
100 }
101 return fmt.Sprintf(fmt.Sprintf(format, width), name)
102 }
138 // `wc` optional WC config
139 func StaticName(name string, wc ...WC) Decorator {
140 return Name(name, wc...)
141 }
142
143 // Name returns name decorator.
144 //
145 // `name` string to display
146 //
147 // `wc` optional WC config
148 func Name(name string, wc ...WC) Decorator {
149 var wc0 WC
150 if len(wc) > 0 {
151 wc0 = wc[0]
152 }
153 return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
154 return wc0.formatMsg(name, widthAccumulator, widthDistributor)
155 })
103156 }
104157
105158 // CountersNoUnit returns raw counters decorator
106159 //
107160 // `pairFormat` printf compatible verbs for current and total, like "%f" or "%d"
108161 //
109 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
110 //
111 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
112 func CountersNoUnit(pairFormat string, width, conf int) DecoratorFunc {
113 return counters(pairFormat, 0, width, conf)
162 // `wc` optional WC config
163 func CountersNoUnit(pairFormat string, wc ...WC) Decorator {
164 return counters(pairFormat, 0, wc...)
114165 }
115166
116167 // CountersKibiByte returns human friendly byte counters decorator, where counters unit is multiple by 1024.
117168 //
118169 // `pairFormat` printf compatible verbs for current and total, like "%f" or "%d"
119170 //
120 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
121 //
122 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
171 // `wc` optional WC config
123172 //
124173 // pairFormat example:
125174 //
126175 // "%.1f / %.1f" = "1.0MiB / 12.0MiB" or "% .1f / % .1f" = "1.0 MiB / 12.0 MiB"
127 func CountersKibiByte(pairFormat string, width, conf int) DecoratorFunc {
128 return counters(pairFormat, unitKiB, width, conf)
176 func CountersKibiByte(pairFormat string, wc ...WC) Decorator {
177 return counters(pairFormat, unitKiB, wc...)
129178 }
130179
131180 // CountersKiloByte returns human friendly byte counters decorator, where counters unit is multiple by 1000.
132181 //
133182 // `pairFormat` printf compatible verbs for current and total, like "%f" or "%d"
134183 //
135 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
136 //
137 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
184 // `wc` optional WC config
138185 //
139186 // pairFormat example:
140187 //
141188 // "%.1f / %.1f" = "1.0MB / 12.0MB" or "% .1f / % .1f" = "1.0 MB / 12.0 MB"
142 func CountersKiloByte(pairFormat string, width, conf int) DecoratorFunc {
143 return counters(pairFormat, unitKB, width, conf)
144 }
145
146 func counters(pairFormat string, unit, width, conf int) DecoratorFunc {
147 format := "%%"
148 if (conf & DidentRight) != 0 {
149 format += "-"
150 }
151 format += "%ds"
152 return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
189 func CountersKiloByte(pairFormat string, wc ...WC) Decorator {
190 return counters(pairFormat, unitKB, wc...)
191 }
192
193 func counters(pairFormat string, unit int, wc ...WC) Decorator {
194 var wc0 WC
195 if len(wc) > 0 {
196 wc0 = wc[0]
197 }
198 return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
153199 var str string
154200 switch unit {
155201 case unitKiB:
159205 default:
160206 str = fmt.Sprintf(pairFormat, s.Current, s.Total)
161207 }
162 if (conf & DwidthSync) != 0 {
163 widthAccumulator <- utf8.RuneCountInString(str)
164 max := <-widthDistributor
165 if (conf & DextraSpace) != 0 {
166 max++
167 }
168 return fmt.Sprintf(fmt.Sprintf(format, max), str)
169 }
170 return fmt.Sprintf(fmt.Sprintf(format, width), str)
171 }
208 return wc0.formatMsg(str, widthAccumulator, widthDistributor)
209 })
172210 }
173211
174212 // ETA returns exponential-weighted-moving-average ETA decorator.
175213 //
176 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
177 //
178 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
179 //
180 // To correctly estimate non io progress, *Bar.StartBlock should be called,
181 // before each increment block.
182 func ETA(width, conf int) DecoratorFunc {
183 format := "%%"
184 if (conf & DidentRight) != 0 {
185 format += "-"
186 }
187 format += "%ds"
188 return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
189 str := fmt.Sprint(time.Duration(s.TimeRemaining.Seconds()) * time.Second)
190 if (conf & DwidthSync) != 0 {
191 widthAccumulator <- utf8.RuneCountInString(str)
192 max := <-widthDistributor
193 if (conf & DextraSpace) != 0 {
194 max++
195 }
196 return fmt.Sprintf(fmt.Sprintf(format, max), str)
197 }
198 return fmt.Sprintf(fmt.Sprintf(format, width), str)
199 }
214 // `style` onfe of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
215 //
216 // `age` is a decay factor alpha for underlying ewma.
217 // General rule of thumb, for the best value:
218 // expected progress time in seconds divided by two.
219 // For example expected progress duration is one hour.
220 // age = 3600 / 2
221 //
222 // `startBlock` is channel, user suppose to send time.Now() on each iteration of block start.
223 //
224 // `wc` optional WC config
225 func ETA(style int, age float64, startBlock chan time.Time, wc ...WC) Decorator {
226 var wc0 WC
227 if len(wc) > 0 {
228 wc0 = wc[0]
229 }
230 if age == .0 {
231 age = ewma.AVG_METRIC_AGE
232 }
233 return &EwmaETA{
234 MovingAverage: ewma.NewMovingAverage(age),
235 StartBlockCh: startBlock,
236 style: style,
237 wc: wc0,
238 }
239 }
240
241 type EwmaETA struct {
242 ewma.MovingAverage
243 StartBlockCh chan time.Time
244 style int
245 wc WC
246 onComplete *struct {
247 msg string
248 wc WC
249 }
250 }
251
252 func (s *EwmaETA) Decor(st *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
253 if st.Completed && s.onComplete != nil {
254 return s.onComplete.wc.formatMsg(s.onComplete.msg, widthAccumulator, widthDistributor)
255 }
256
257 var str string
258 timeRemaining := time.Duration(float64(st.Total-st.Current) * s.MovingAverage.Value())
259 hours := int64((timeRemaining / time.Hour) % 60)
260 minutes := int64((timeRemaining / time.Minute) % 60)
261 seconds := int64((timeRemaining / time.Second) % 60)
262
263 switch s.style {
264 case ET_STYLE_GO:
265 str = fmt.Sprint(time.Duration(timeRemaining.Seconds()) * time.Second)
266 case ET_STYLE_HHMMSS:
267 str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
268 case ET_STYLE_HHMM:
269 str = fmt.Sprintf("%02d:%02d", hours, minutes)
270 case ET_STYLE_MMSS:
271 str = fmt.Sprintf("%02d:%02d", minutes, seconds)
272 }
273
274 return s.wc.formatMsg(str, widthAccumulator, widthDistributor)
275 }
276
277 func (s *EwmaETA) OnComplete(msg string, wc ...WC) {
278 var wc0 WC
279 if len(wc) > 0 {
280 wc0 = wc[0]
281 }
282 s.onComplete = &struct {
283 msg string
284 wc WC
285 }{msg, wc0}
200286 }
201287
202288 // Elapsed returns elapsed time decorator.
203289 //
204 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
205 //
206 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
207 func Elapsed(width, conf int) DecoratorFunc {
208 format := "%%"
209 if (conf & DidentRight) != 0 {
210 format += "-"
211 }
212 format += "%ds"
213 return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
214 str := fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second)
215 if (conf & DwidthSync) != 0 {
216 widthAccumulator <- utf8.RuneCountInString(str)
217 max := <-widthDistributor
218 if (conf & DextraSpace) != 0 {
219 max++
220 }
221 return fmt.Sprintf(fmt.Sprintf(format, max), str)
222 }
223 return fmt.Sprintf(fmt.Sprintf(format, width), str)
224 }
290 // `style` onfe of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
291 //
292 // `wc` optional WC config
293 func Elapsed(style int, wc ...WC) Decorator {
294 var wc0 WC
295 if len(wc) > 0 {
296 wc0 = wc[0]
297 }
298 return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
299 var str string
300 hours := int64((s.TimeElapsed / time.Hour) % 60)
301 minutes := int64((s.TimeElapsed / time.Minute) % 60)
302 seconds := int64((s.TimeElapsed / time.Second) % 60)
303
304 switch style {
305 case ET_STYLE_GO:
306 str = fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second)
307 case ET_STYLE_HHMMSS:
308 str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
309 case ET_STYLE_HHMM:
310 str = fmt.Sprintf("%02d:%02d", hours, minutes)
311 case ET_STYLE_MMSS:
312 str = fmt.Sprintf("%02d:%02d", minutes, seconds)
313 }
314 return wc0.formatMsg(str, widthAccumulator, widthDistributor)
315 })
225316 }
226317
227318 // Percentage returns percentage decorator.
228319 //
229 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
230 //
231 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
232 func Percentage(width, conf int) DecoratorFunc {
233 format := "%%"
234 if (conf & DidentRight) != 0 {
235 format += "-"
236 }
237 format += "%ds"
238 return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
320 // `wc` optional WC config
321 func Percentage(wc ...WC) Decorator {
322 var wc0 WC
323 if len(wc) > 0 {
324 wc0 = wc[0]
325 }
326 return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
239327 str := fmt.Sprintf("%d %%", CalcPercentage(s.Total, s.Current, 100))
240 if (conf & DwidthSync) != 0 {
241 widthAccumulator <- utf8.RuneCountInString(str)
242 max := <-widthDistributor
243 if (conf & DextraSpace) != 0 {
244 max++
245 }
246 return fmt.Sprintf(fmt.Sprintf(format, max), str)
247 }
248 return fmt.Sprintf(fmt.Sprintf(format, width), str)
249 }
328 return wc0.formatMsg(str, widthAccumulator, widthDistributor)
329 })
250330 }
251331
252332 // CalcPercentage is a helper function, to calculate percentage.
273353 //
274354 // `unitFormat` printf compatible verb for value, like "%f" or "%d"
275355 //
276 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
277 //
278 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
356 // `wc` optional WC config
279357 //
280358 // unitFormat example:
281359 //
282360 // "%.1f" = "1.0" or "% .1f" = "1.0"
283 func SpeedNoUnit(unitFormat string, width, conf int) DecoratorFunc {
284 return speed(unitFormat, 0, width, conf)
361 func SpeedNoUnit(unitFormat string, wc ...WC) Decorator {
362 return speed(unitFormat, 0, wc...)
285363 }
286364
287365 // SpeedKibiByte returns human friendly I/O operation speed decorator,
288366 //
289367 // `unitFormat` printf compatible verb for value, like "%f" or "%d"
290368 //
291 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
292 //
293 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
369 // `wc` optional WC config
294370 //
295371 // unitFormat example:
296372 //
297373 // "%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s"
298 func SpeedKibiByte(unitFormat string, width, conf int) DecoratorFunc {
299 return speed(unitFormat, unitKiB, width, conf)
374 func SpeedKibiByte(unitFormat string, wc ...WC) Decorator {
375 return speed(unitFormat, unitKiB, wc...)
300376 }
301377
302378 // SpeedKiloByte returns human friendly I/O operation speed decorator,
303379 //
304380 // `unitFormat` printf compatible verb for value, like "%f" or "%d"
305381 //
306 // `width` width reservation to apply, ignored if `DwidthSync` bit is set
307 //
308 // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace]
382 // `wc` optional WC config
309383 //
310384 // unitFormat example:
311385 //
312386 // "%.1f" = "1.0MB/s" or "% .1f" = "1.0 MB/s"
313 func SpeedKiloByte(unitFormat string, width, conf int) DecoratorFunc {
314 return speed(unitFormat, unitKB, width, conf)
315 }
316
317 func speed(unitFormat string, unit, width, conf int) DecoratorFunc {
318 format := "%%"
319 if (conf & DidentRight) != 0 {
320 format += "-"
321 }
322 format += "%ds"
323
324 return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
387 func SpeedKiloByte(unitFormat string, wc ...WC) Decorator {
388 return speed(unitFormat, unitKB, wc...)
389 }
390
391 func speed(unitFormat string, unit int, wc ...WC) Decorator {
392 var wc0 WC
393 if len(wc) > 0 {
394 wc0 = wc[0]
395 }
396 return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
325397 var str string
326
327398 speed := float64(s.Current) / s.TimeElapsed.Seconds()
328
329399 if math.IsNaN(speed) || math.IsInf(speed, 0) {
330400 speed = .0
331401 }
338408 default:
339409 str = fmt.Sprintf(unitFormat, speed)
340410 }
341 if (conf & DwidthSync) != 0 {
342 widthAccumulator <- utf8.RuneCountInString(str)
343 max := <-widthDistributor
344 if (conf & DextraSpace) != 0 {
345 max++
346 }
347 return fmt.Sprintf(fmt.Sprintf(format, max), str)
348 }
349 return fmt.Sprintf(fmt.Sprintf(format, width), str)
350 }
351 }
411 return wc0.formatMsg(str, widthAccumulator, widthDistributor)
412 })
413 }
88 "github.com/vbauerster/mpb/decor"
99 )
1010
11 func TestStaticName(t *testing.T) {
11 func TestNameDecorator(t *testing.T) {
1212 tests := []struct {
13 fn decor.DecoratorFunc
14 want string
13 decorator decor.Decorator
14 want string
1515 }{
1616 {
17 fn: decor.StaticName("Test", 0, 0),
18 want: "Test",
19 },
20 {
21 fn: decor.StaticName("Test", len("Test"), 0),
22 want: "Test",
23 },
24 {
25 fn: decor.StaticName("Test", 10, 0),
26 want: " Test",
27 },
28 {
29 fn: decor.StaticName("Test", 10, decor.DidentRight),
30 want: "Test ",
17 decorator: decor.Name("Test"),
18 want: "Test",
19 },
20 {
21 decorator: decor.Name("Test", decor.WC{W: len("Test")}),
22 want: "Test",
23 },
24 {
25 decorator: decor.Name("Test", decor.WC{W: 10}),
26 want: " Test",
27 },
28 {
29 decorator: decor.Name("Test", decor.WC{W: 10, C: decor.DidentRight}),
30 want: "Test ",
3131 },
3232 }
3333
3434 for _, test := range tests {
35 got := test.fn(nil, nil, nil)
35 got := test.decorator.Decor(nil, nil, nil)
3636 if got != test.want {
3737 t.Errorf("Want: %q, Got: %q\n", test.want, got)
3838 }
4040 }
4141
4242 type step struct {
43 stat *decor.Statistics
44 dfn decor.DecoratorFunc
45 want string
43 stat *decor.Statistics
44 decorator decor.Decorator
45 want string
4646 }
4747
4848 func TestPercentageDwidthSync(t *testing.T) {
5151 []step{
5252 {
5353 &decor.Statistics{Total: 100, Current: 8},
54 decor.Percentage(0, decor.DwidthSync),
54 decor.Percentage(decor.WCSyncWidth),
5555 "8 %",
5656 },
5757 {
5858 &decor.Statistics{Total: 100, Current: 9},
59 decor.Percentage(0, decor.DwidthSync),
59 decor.Percentage(decor.WCSyncWidth),
6060 "9 %",
6161 },
6262 },
6363 []step{
6464 {
6565 &decor.Statistics{Total: 100, Current: 9},
66 decor.Percentage(0, decor.DwidthSync),
66 decor.Percentage(decor.WCSyncWidth),
6767 " 9 %",
6868 },
6969 {
7070 &decor.Statistics{Total: 100, Current: 10},
71 decor.Percentage(0, decor.DwidthSync),
71 decor.Percentage(decor.WCSyncWidth),
7272 "10 %",
7373 },
7474 },
7575 []step{
7676 {
7777 &decor.Statistics{Total: 100, Current: 9},
78 decor.Percentage(0, decor.DwidthSync),
78 decor.Percentage(decor.WCSyncWidth),
7979 " 9 %",
8080 },
8181 {
8282 &decor.Statistics{Total: 100, Current: 100},
83 decor.Percentage(0, decor.DwidthSync),
83 decor.Percentage(decor.WCSyncWidth),
8484 "100 %",
8585 },
8686 },
9595 []step{
9696 {
9797 &decor.Statistics{Total: 100, Current: 8},
98 decor.Percentage(0, decor.DwidthSync|decor.DidentRight),
98 decor.Percentage(decor.WCSyncWidthR),
9999 "8 %",
100100 },
101101 {
102102 &decor.Statistics{Total: 100, Current: 9},
103 decor.Percentage(0, decor.DwidthSync|decor.DidentRight),
103 decor.Percentage(decor.WCSyncWidthR),
104104 "9 %",
105105 },
106106 },
107107 []step{
108108 {
109109 &decor.Statistics{Total: 100, Current: 9},
110 decor.Percentage(0, decor.DwidthSync|decor.DidentRight),
110 decor.Percentage(decor.WCSyncWidthR),
111111 "9 % ",
112112 },
113113 {
114114 &decor.Statistics{Total: 100, Current: 10},
115 decor.Percentage(0, decor.DwidthSync|decor.DidentRight),
115 decor.Percentage(decor.WCSyncWidthR),
116116 "10 %",
117117 },
118118 },
119119 []step{
120120 {
121121 &decor.Statistics{Total: 100, Current: 9},
122 decor.Percentage(0, decor.DwidthSync|decor.DidentRight),
122 decor.Percentage(decor.WCSyncWidthR),
123123 "9 % ",
124124 },
125125 {
126126 &decor.Statistics{Total: 100, Current: 100},
127 decor.Percentage(0, decor.DwidthSync|decor.DidentRight),
127 decor.Percentage(decor.WCSyncWidthR),
128128 "100 %",
129129 },
130130 },
139139 []step{
140140 {
141141 &decor.Statistics{Total: 100, Current: 8},
142 decor.Percentage(0, decor.DSyncSpace),
142 decor.Percentage(decor.WCSyncSpace),
143143 " 8 %",
144144 },
145145 {
146146 &decor.Statistics{Total: 100, Current: 9},
147 decor.Percentage(0, decor.DSyncSpace),
147 decor.Percentage(decor.WCSyncSpace),
148148 " 9 %",
149149 },
150150 },
151151 []step{
152152 {
153153 &decor.Statistics{Total: 100, Current: 9},
154 decor.Percentage(0, decor.DSyncSpace),
154 decor.Percentage(decor.WCSyncSpace),
155155 " 9 %",
156156 },
157157 {
158158 &decor.Statistics{Total: 100, Current: 10},
159 decor.Percentage(0, decor.DSyncSpace),
159 decor.Percentage(decor.WCSyncSpace),
160160 " 10 %",
161161 },
162162 },
163163 []step{
164164 {
165165 &decor.Statistics{Total: 100, Current: 9},
166 decor.Percentage(0, decor.DSyncSpace),
166 decor.Percentage(decor.WCSyncSpace),
167167 " 9 %",
168168 },
169169 {
170170 &decor.Statistics{Total: 100, Current: 100},
171 decor.Percentage(0, decor.DSyncSpace),
171 decor.Percentage(decor.WCSyncSpace),
172172 " 100 %",
173173 },
174174 },
196196 res[i] = make(chan string, 1)
197197 go func(s step, ch chan string) {
198198 defer wg.Done()
199 ch <- s.dfn(s.stat, ws.Accumulator[0], ws.Distributor[0])
199 ch <- s.decorator.Decor(s.stat, ws.Accumulator[0], ws.Distributor[0])
200200 }(columnCase[i], res[i])
201201 }
202202 wg.Wait()
1313 func Example() {
1414 p := mpb.New(
1515 // override default (80) width
16 mpb.WithWidth(100),
16 mpb.WithWidth(64),
1717 // override default "[=>-]" format
1818 mpb.WithFormat("╢▌▌░╟"),
1919 // override default 120ms refresh rate
20 mpb.WithRefreshRate(100*time.Millisecond),
20 mpb.WithRefreshRate(180*time.Millisecond),
2121 )
2222
2323 total := 100
2424 name := "Single Bar:"
25 startBlock := make(chan time.Time)
2526 // adding a single bar
2627 bar := p.AddBar(int64(total),
2728 mpb.PrependDecorators(
28 // Display our static name with one space on the right
29 decor.StaticName(name, len(name)+1, decor.DidentRight),
30 // ETA decorator with width reservation of 3 runes
31 decor.ETA(3, 0),
29 // Display our name with one space on the right
30 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
31 // Replace ETA decorator with message, OnComplete event
32 decor.OnComplete(
33 // ETA decorator with default eta age, and width reservation of 4
34 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 4}),
35 "done",
36 ),
3237 ),
3338 mpb.AppendDecorators(
34 // Percentage decorator with width reservation of 5 runes
35 decor.Percentage(5, 0),
39 decor.Percentage(),
3640 ),
3741 )
3842
3943 // simulating some work
4044 max := 100 * time.Millisecond
4145 for i := 0; i < total; i++ {
42 bar.StartBlock() // optional call, required for ETA
46 // update start block time, required for ETA calculation
47 startBlock <- time.Now()
4348 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
44 // increment by 1 (there is bar.IncrBy(int) method, if needed)
49 // Increment by 1 (there is bar.IncrBy(int) method, if needed)
4550 bar.Increment()
4651 }
4752 // wait for our bar to complete and flush
7075 // Assuming ContentLength > 0
7176 bar := p.AddBar(resp.ContentLength,
7277 mpb.AppendDecorators(
73 decor.CountersKibiByte("%6.1f / %6.1f", 12, 0),
78 decor.CountersKibiByte("%6.1f / %6.1f"),
7479 ),
7580 )
7681
3131
3232 for i := 0; i < numBars; i++ {
3333 name := fmt.Sprintf("Bar#%d:", i)
34 startBlock := make(chan time.Time)
3435 bar := p.AddBar(int64(total),
3536 mpb.PrependDecorators(
36 decor.StaticName(name, 0, decor.DwidthSync|decor.DidentRight),
37 decor.ETA(4, decor.DSyncSpace),
37 decor.Name(name),
38 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncSpace),
3839 ),
3940 mpb.AppendDecorators(
40 decor.Percentage(5, 0),
41 decor.Percentage(decor.WC{W: 5}),
4142 ),
4243 )
4344
4546 defer wg.Done()
4647 max := 100 * time.Millisecond
4748 for !bar.Completed() {
48 bar.StartBlock()
49 startBlock <- time.Now()
4950 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
5051 bar.Increment()
5152 }
2929 b := p.AddBar(rand.Int63n(201)+100,
3030 mpb.BarRemoveOnComplete(),
3131 mpb.PrependDecorators(
32 decor.StaticName(task, len(task)+1, decor.DidentRight),
33 decor.StaticName(job, 0, decor.DSyncSpaceR),
34 decor.CountersNoUnit("%d / %d", 0, decor.DwidthSync),
32 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
33 decor.Name(job, decor.WCSyncSpaceR),
34 decor.CountersNoUnit("%d / %d", decor.WCSyncWidth),
3535 ),
36 mpb.AppendDecorators(decor.Percentage(5, 0)),
36 mpb.AppendDecorators(decor.Percentage(decor.WC{W: 5})),
3737 )
38 go newTask(wg, b, i+1)
38 go newTask(wg, b, i+1, nil)
3939 bars = append(bars, b)
4040 }
4141
4343 doneWg.Add(1)
4444 i := i
4545 go func() {
46 startBlock := make(chan time.Time)
4647 task := fmt.Sprintf("Task#%02d:", i)
4748 job := "installing"
4849 // preparing delayed bars
5051 mpb.BarReplaceOnComplete(bars[i]),
5152 mpb.BarClearOnComplete(),
5253 mpb.PrependDecorators(
53 decor.StaticName(task, len(task)+1, decor.DidentRight),
54 decor.OnComplete(decor.StaticName(job, 0, decor.DSyncSpaceR), "done!", 0, decor.DSyncSpaceR),
55 decor.OnComplete(decor.ETA(0, decor.DwidthSync), "", 0, decor.DwidthSync),
54 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
55 decor.OnComplete(decor.Name(job, decor.WCSyncSpaceR), "done!", decor.WCSyncSpaceR),
56 decor.OnComplete(
57 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncWidth),
58 "",
59 decor.WCSyncSpace,
60 ),
5661 ),
5762 mpb.AppendDecorators(
58 decor.OnComplete(decor.Percentage(5, 0), "", 0, 0),
63 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
5964 ),
6065 )
6166 // waiting for download to complete, before starting install job
6267 downloadWgg[i].Wait()
63 go newTask(doneWg, b, numBars-i)
68 go newTask(doneWg, b, numBars-i, startBlock)
6469 }()
6570 }
6671
6772 p.Wait()
6873 }
6974
70 func newTask(wg *sync.WaitGroup, b *mpb.Bar, incrBy int) {
75 func newTask(wg *sync.WaitGroup, b *mpb.Bar, incrBy int, startBlock chan<- time.Time) {
7176 defer wg.Done()
7277 max := 100 * time.Millisecond
7378 for !b.Completed() {
74 b.StartBlock()
79 if startBlock != nil {
80 startBlock <- time.Now()
81 }
7582 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
7683 b.IncrBy(incrBy)
7784 }
2121 // trigger total auto increment by 1, when 18 % remains till bar completion
2222 mpb.BarAutoIncrTotal(18, 1),
2323 mpb.PrependDecorators(
24 decor.CountersNoUnit("%d / %d", 12, 0),
24 decor.CountersNoUnit("%d / %d", decor.WC{W: 12}),
2525 ),
2626 mpb.AppendDecorators(
27 decor.Percentage(4, 0),
27 decor.Percentage(),
2828 ),
2929 )
3030
77 "os"
88 "path/filepath"
99 "sync"
10 "time"
1011
1112 "github.com/vbauerster/mpb"
1213 "github.com/vbauerster/mpb/decor"
5657 return
5758 }
5859
60 startBlock := make(chan time.Time)
61
5962 // create bar with appropriate decorators
6063 bar := p.AddBar(size, mpb.BarPriority(n),
6164 mpb.PrependDecorators(
62 decor.StaticName(name, len(name)+1, decor.DidentRight),
63 decor.CountersKibiByte("%6.1f / %6.1f", 0, decor.DwidthSync),
65 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
66 decor.CountersKibiByte("%6.1f / %6.1f", decor.WCSyncWidth),
6467 ),
6568 mpb.AppendDecorators(
66 decor.ETA(0, decor.DwidthSync),
67 decor.SpeedKibiByte("%6.1f", 18, 0),
69 decor.ETA(decor.ET_STYLE_HHMMSS, 60, startBlock, decor.WCSyncWidth),
70 decor.SpeedKibiByte("%6.1f", decor.WCSyncSpace),
6871 ),
6972 )
7073
7174 // create proxy reader
72 reader := bar.ProxyReader(resp.Body)
75 reader := bar.ProxyReader(resp.Body, startBlock)
7376 // and copy from reader
7477 _, err = io.Copy(dest, reader)
7578
55 "net/http"
66 "os"
77 "path/filepath"
8 "time"
89
910 "github.com/vbauerster/mpb"
1011 "github.com/vbauerster/mpb/decor"
1112 )
1213
1314 func main() {
14 url := "https://homebrew.bintray.com/bottles/libtiff-4.0.7.sierra.bottle.tar.gz"
15 url := "https://github.com/onivim/oni/releases/download/v0.3.4/Oni-0.3.4-osx.dmg"
1516
1617 resp, err := http.Get(url)
1718 if err != nil {
3738
3839 p := mpb.New(mpb.WithWidth(64))
3940
41 startBlock := make(chan time.Time)
4042 bar := p.AddBar(size,
4143 mpb.PrependDecorators(
42 decor.CountersKibiByte("% 6.1f / % 6.1f", 18, 0),
44 decor.CountersKibiByte("% 6.1f / % 6.1f", decor.WC{W: 18}),
4345 ),
4446 mpb.AppendDecorators(
45 decor.ETA(0, 0),
46 decor.SpeedKibiByte("% 6.1f", 18, 0),
47 decor.ETA(decor.ET_STYLE_HHMMSS, 900, startBlock),
48 decor.SpeedKibiByte("% 6.1f", decor.WC{W: 14}),
4749 ),
4850 )
4951
5052 // create proxy reader
51 reader := bar.ProxyReader(resp.Body)
53 reader := bar.ProxyReader(resp.Body, startBlock)
5254
5355 // and copy from reader, ignoring errors
5456 io.Copy(dest, reader)
2020 for i := 0; i < numBars; i++ {
2121 name := fmt.Sprintf("b#%02d:", i)
2222 bar := p.AddBar(100, mpb.BarID(i), mpb.PrependDecorators(
23 func(s *decor.Statistics, _ chan<- int, _ <-chan int) string {
23 decor.DecoratorFunc(func(s *decor.Statistics, _ chan<- int, _ <-chan int) string {
2424 // s.Current == 42 may never happen, if sleep btw increments is
2525 // too short, thus using s.Current >= 42
2626 if s.ID == 1 && s.Current >= 42 {
2727 panic(wantPanic)
2828 }
2929 return name
30 },
30 }),
3131 ))
3232
3333 go func() {
2525 if i != 1 {
2626 name = fmt.Sprintf("Bar#%d:", i)
2727 }
28 startBlock := make(chan time.Time)
2829 b := p.AddBar(int64(total),
2930 mpb.PrependDecorators(
30 decor.StaticName(name, 0, decor.DwidthSync),
31 decor.OnComplete(decor.ETA(4, 0), "Done", 0, decor.DSyncSpace),
31 decor.Name(name, decor.WCSyncWidth),
32 decor.OnComplete(
33 decor.ETA(decor.ET_STYLE_MMSS, 0, startBlock, decor.WC{W: 6}),
34 "Done",
35 decor.WCSyncSpace,
36 ),
3237 ),
3338 mpb.AppendDecorators(
34 decor.Percentage(5, 0),
39 decor.Percentage(decor.WC{W: 5}),
3540 ),
3641 )
3742 go func() {
3843 defer wg.Done()
3944 max := 100 * time.Millisecond
4045 for i := 0; i < total; i++ {
41 b.StartBlock()
46 startBlock <- time.Now()
4247 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4348 b.Increment()
4449 }
2727 }
2828 b := p.AddBar(int64(total),
2929 mpb.PrependDecorators(
30 decor.StaticName(name, 0, decor.DwidthSync|decor.DidentRight),
31 decor.Elapsed(3, decor.DSyncSpace),
30 decor.Name(name, decor.WCSyncWidthR),
31 decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncSpace),
3232 ),
3333 mpb.AppendDecorators(
34 decor.Percentage(5, 0),
34 decor.Percentage(decor.WC{W: 5}),
3535 ),
3636 )
3737 go func() {
2121 wg.Add(numBars)
2222
2323 for i := 0; i < numBars; i++ {
24 var name string
25 name = fmt.Sprintf("Bar#%d:", i)
24 name := fmt.Sprintf("Bar#%d:", i)
2625
2726 var bOption mpb.BarOption
2827 if i == 0 {
2928 bOption = mpb.BarRemoveOnComplete()
3029 }
3130
31 startBlock := make(chan time.Time)
3232 b := p.AddBar(int64(total), mpb.BarID(i),
3333 bOption,
3434 mpb.PrependDecorators(
35 decor.StaticName(name, 0, decor.DwidthSync|decor.DidentRight),
36 decor.ETA(4, decor.DSyncSpace),
35 decor.Name(name),
36 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncSpace),
3737 ),
3838 mpb.AppendDecorators(
39 decor.Percentage(5, 0),
39 decor.Percentage(),
4040 ),
4141 )
4242 go func() {
4343 defer wg.Done()
4444 max := 100 * time.Millisecond
4545 for i := 0; i < total; i++ {
46 b.StartBlock()
46 startBlock <- time.Now()
4747 if b.ID() == 2 && i == 42 {
4848 p.Abort(b)
4949 return
2121
2222 for i := 0; i < numBars; i++ {
2323 name := fmt.Sprintf("Bar#%d:", i)
24 startBlock := make(chan time.Time)
2425 bar := p.AddBar(int64(total),
2526 mpb.PrependDecorators(
26 // Display our static name with one space on the right
27 decor.StaticName(name, len(name)+1, decor.DidentRight),
28 // DwidthSync bit enables same column width synchronization
29 decor.Percentage(0, decor.DwidthSync),
27 // Display our name with one space on the right
28 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
29 // decor.DSyncWidth bit enables same column width synchronization
30 decor.Percentage(decor.WCSyncWidth),
3031 ),
3132 mpb.AppendDecorators(
32 // replace our ETA decorator with "done!", on bar completion event
33 decor.OnComplete(decor.ETA(3, 0), "done!", 0, 0),
33 // Replace ETA decorator with message, OnComplete event
34 decor.OnComplete(
35 // ETA decorator with default eta age, and width reservation of 3
36 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 3}),
37 "done!",
38 ),
3439 ),
3540 )
3641 // simulating some work
3843 defer wg.Done()
3944 max := 100 * time.Millisecond
4045 for i := 0; i < total; i++ {
41 bar.StartBlock()
46 startBlock <- time.Now()
4247 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4348 bar.Increment()
4449 }
4550 }()
4651 }
47 // first wait for provided wg, then
4852 // wait for all bars to complete and flush
4953 p.Wait()
5054 }
1919
2020 total := 100
2121 name := "Single Bar:"
22 startBlock := make(chan time.Time)
2223 // adding a single bar
2324 bar := p.AddBar(int64(total),
2425 mpb.PrependDecorators(
25 // Display our static name with one space on the right
26 decor.StaticName(name, len(name)+1, decor.DidentRight),
27 // ETA decorator with width reservation of 3 runes
28 // decor.ETA(3, 0),
29 decor.OnComplete(decor.ETA(4, 0), "done", 0, 0),
26 // Display our name with one space on the right
27 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
28 // Replace ETA decorator with message, OnComplete event
29 decor.OnComplete(
30 // ETA decorator with default eta age, and width reservation of 4
31 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 4}),
32 "done",
33 ),
3034 ),
3135 mpb.AppendDecorators(
32 // Percentage decorator with width reservation of 5 runes
33 decor.Percentage(5, 0),
36 decor.Percentage(),
3437 ),
3538 )
3639
3740 // simulating some work
3841 max := 100 * time.Millisecond
3942 for i := 0; i < total; i++ {
40 bar.StartBlock() // optional call, required for ETA
43 // update start block time, required for ETA calculation
44 startBlock <- time.Now()
4145 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4246 // Increment by 1 (there is bar.IncrBy(int) method, if needed)
4347 bar.Increment()
2525 if i != 1 {
2626 name = fmt.Sprintf("Bar#%d:", i)
2727 }
28 startBlock := make(chan time.Time)
2829 b := p.AddBar(int64(total),
2930 mpb.PrependDecorators(
30 decor.StaticName(name, 0, decor.DwidthSync),
31 decor.CountersNoUnit("%d / %d", 10, decor.DSyncSpace),
31 decor.Name(name, decor.WCSyncWidth),
32 decor.CountersNoUnit("%d / %d", decor.WCSyncSpace),
3233 ),
3334 mpb.AppendDecorators(
34 decor.ETA(3, 0),
35 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 3}),
3536 ),
3637 )
3738 go func() {
3839 defer wg.Done()
3940 max := 100 * time.Millisecond
4041 for i := 0; i < total; i++ {
41 b.StartBlock()
42 startBlock <- time.Now()
4243 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4344 if i&1 == 1 {
4445 priority := total - int(b.Current())
2626 for i := 0; i < totalBars; i++ {
2727 name := fmt.Sprintf("Bar#%02d: ", i)
2828 total := rand.Intn(120) + 10
29 startBlock := make(chan time.Time)
2930 bar := p.AddBar(int64(total),
3031 mpb.PrependDecorators(
31 decor.StaticName(name, len(name), 0),
32 decor.ETA(4, decor.DSyncSpace),
32 decor.Name(name),
33 decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncSpace),
3334 ),
3435 mpb.AppendDecorators(
35 decor.Percentage(5, 0),
36 decor.Percentage(decor.WC{W: 5}),
3637 ),
3738 )
3839
4041 defer wg.Done()
4142 max := 100 * time.Millisecond
4243 for i := 0; i < total; i++ {
43 bar.StartBlock()
44 startBlock <- time.Now()
4445 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4546 bar.Increment()
4647 }
00 package mpb
11
2 import "io"
2 import (
3 "io"
4 "time"
5 )
36
47 // Reader is io.Reader wrapper, for proxy read bytes
58 type Reader struct {
69 io.Reader
7 bar *Bar
10 bar *Bar
11 startBlockCh chan<- time.Time
812 }
913
1014 func (r *Reader) Read(p []byte) (int, error) {
11 r.bar.StartBlock()
15 if r.startBlockCh != nil {
16 r.startBlockCh <- time.Now()
17 }
1218 n, err := r.Reader.Read(p)
1319 r.bar.IncrBy(n)
1420 return n, err