BarExtender
Vladimir Bauer
7 years ago
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "io" | |
| 5 | "math/rand" | |
| 6 | "sync" | |
| 7 | "time" | |
| 8 | ||
| 9 | "github.com/vbauerster/mpb/v4" | |
| 10 | "github.com/vbauerster/mpb/v4/decor" | |
| 11 | ) | |
| 12 | ||
| 13 | func init() { | |
| 14 | rand.Seed(time.Now().UnixNano()) | |
| 15 | } | |
| 16 | ||
| 17 | func main() { | |
| 18 | var wg sync.WaitGroup | |
| 19 | p := mpb.New(mpb.WithWaitGroup(&wg)) | |
| 20 | total, numBars := 100, 3 | |
| 21 | wg.Add(numBars) | |
| 22 | ||
| 23 | for i := 0; i < numBars; i++ { | |
| 24 | name := fmt.Sprintf("Bar#%d:", i) | |
| 25 | efn := func(w io.Writer, width int, s *decor.Statistics) { | |
| 26 | if s.Completed { | |
| 27 | fmt.Fprintf(w, "Bar id: %d has been completed\n", s.ID) | |
| 28 | } | |
| 29 | } | |
| 30 | bar := p.AddBar(int64(total), mpb.BarExtender(mpb.FillerFunc(efn)), | |
| 31 | mpb.PrependDecorators( | |
| 32 | // simple name decorator | |
| 33 | decor.Name(name), | |
| 34 | // decor.DSyncWidth bit enables column width synchronization | |
| 35 | decor.Percentage(decor.WCSyncSpace), | |
| 36 | ), | |
| 37 | mpb.AppendDecorators( | |
| 38 | // replace ETA decorator with "done" message, OnComplete event | |
| 39 | decor.OnComplete( | |
| 40 | // ETA decorator with ewma age of 60 | |
| 41 | decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", | |
| 42 | ), | |
| 43 | ), | |
| 44 | ) | |
| 45 | // simulating some work | |
| 46 | go func() { | |
| 47 | defer wg.Done() | |
| 48 | max := 100 * time.Millisecond | |
| 49 | for i := 0; i < total; i++ { | |
| 50 | start := time.Now() | |
| 51 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) | |
| 52 | // ewma based decorators require work duration measurement | |
| 53 | bar.IncrBy(1, time.Since(start)) | |
| 54 | } | |
| 55 | }() | |
| 56 | } | |
| 57 | // wait for all bars to complete and flush | |
| 58 | p.Wait() | |
| 59 | } |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "io" | |
| 5 | "math/rand" | |
| 6 | "sync" | |
| 7 | "time" | |
| 8 | ||
| 9 | "github.com/vbauerster/mpb/v4" | |
| 10 | "github.com/vbauerster/mpb/v4/decor" | |
| 11 | ) | |
| 12 | ||
| 13 | func init() { | |
| 14 | rand.Seed(time.Now().UnixNano()) | |
| 15 | } | |
| 16 | ||
| 17 | func main() { | |
| 18 | var wg sync.WaitGroup | |
| 19 | p := mpb.New(mpb.WithWaitGroup(&wg)) | |
| 20 | total, numBars := 100, 3 | |
| 21 | wg.Add(numBars) | |
| 22 | ||
| 23 | for i := 0; i < numBars; i++ { | |
| 24 | name := fmt.Sprintf("Bar#%d:", i) | |
| 25 | efn := func(w io.Writer, s *decor.Statistics) { | |
| 26 | if s.Completed { | |
| 27 | fmt.Fprintf(w, "Bar id: %d has been completed\n", s.ID) | |
| 28 | } | |
| 29 | } | |
| 30 | bar := p.AddBar(int64(total), mpb.BarNewLineExtend(efn), | |
| 31 | mpb.PrependDecorators( | |
| 32 | // simple name decorator | |
| 33 | decor.Name(name), | |
| 34 | // decor.DSyncWidth bit enables column width synchronization | |
| 35 | decor.Percentage(decor.WCSyncSpace), | |
| 36 | ), | |
| 37 | mpb.AppendDecorators( | |
| 38 | // replace ETA decorator with "done" message, OnComplete event | |
| 39 | decor.OnComplete( | |
| 40 | // ETA decorator with ewma age of 60 | |
| 41 | decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", | |
| 42 | ), | |
| 43 | ), | |
| 44 | ) | |
| 45 | // simulating some work | |
| 46 | go func() { | |
| 47 | defer wg.Done() | |
| 48 | max := 100 * time.Millisecond | |
| 49 | for i := 0; i < total; i++ { | |
| 50 | start := time.Now() | |
| 51 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) | |
| 52 | // ewma based decorators require work duration measurement | |
| 53 | bar.IncrBy(1, time.Since(start)) | |
| 54 | } | |
| 55 | }() | |
| 56 | } | |
| 57 | // wait for all bars to complete and flush | |
| 58 | p.Wait() | |
| 59 | } |
| 13 | 13 | "github.com/vbauerster/mpb/v4/decor" |
| 14 | 14 | ) |
| 15 | 15 | |
| 16 | // Bar represents a progress Bar | |
| 16 | // Filler interface. | |
| 17 | // Bar renders by calling Filler's Fill method. You can literally have | |
| 18 | // any bar kind, by implementing this interface and passing it to the | |
| 19 | // Add method. | |
| 20 | type Filler interface { | |
| 21 | Fill(w io.Writer, termWidth int, stat *decor.Statistics) | |
| 22 | } | |
| 23 | ||
| 24 | // FillerFunc is function type adapter to convert function into Filler. | |
| 25 | type FillerFunc func(w io.Writer, termWidth int, stat *decor.Statistics) | |
| 26 | ||
| 27 | func (f FillerFunc) Fill(w io.Writer, termWidth int, stat *decor.Statistics) { | |
| 28 | f(w, termWidth, stat) | |
| 29 | } | |
| 30 | ||
| 31 | // Bar represents a progress Bar. | |
| 17 | 32 | type Bar struct { |
| 18 | 33 | priority int |
| 19 | 34 | index int |
| 32 | 47 | shutdown chan struct{} |
| 33 | 48 | } |
| 34 | 49 | |
| 35 | // Filler interface. | |
| 36 | // Bar renders by calling Filler's Fill method. You can literally have | |
| 37 | // any bar kind, by implementing this interface and passing it to the | |
| 38 | // Add method. | |
| 39 | type Filler interface { | |
| 40 | Fill(w io.Writer, width int, s *decor.Statistics) | |
| 41 | } | |
| 42 | ||
| 43 | 50 | type ( |
| 44 | 51 | bState struct { |
| 45 | filler Filler | |
| 46 | id int | |
| 47 | width int | |
| 48 | alignment int | |
| 49 | total int64 | |
| 50 | current int64 | |
| 51 | trimSpace bool | |
| 52 | toComplete bool | |
| 53 | removeOnComplete bool | |
| 54 | barClearOnComplete bool | |
| 55 | completeFlushed bool | |
| 56 | aDecorators []decor.Decorator | |
| 57 | pDecorators []decor.Decorator | |
| 58 | amountReceivers []decor.AmountReceiver | |
| 59 | shutdownListeners []decor.ShutdownListener | |
| 60 | bufP, bufB, bufA *bytes.Buffer | |
| 61 | bufNL *bytes.Buffer | |
| 62 | panicMsg string | |
| 63 | newLineExtendFn func(io.Writer, *decor.Statistics) | |
| 52 | filler Filler | |
| 53 | extender Filler | |
| 54 | id int | |
| 55 | width int | |
| 56 | alignment int | |
| 57 | total int64 | |
| 58 | current int64 | |
| 59 | trimSpace bool | |
| 60 | toComplete bool | |
| 61 | removeOnComplete bool | |
| 62 | barClearOnComplete bool | |
| 63 | completeFlushed bool | |
| 64 | aDecorators []decor.Decorator | |
| 65 | pDecorators []decor.Decorator | |
| 66 | amountReceivers []decor.AmountReceiver | |
| 67 | shutdownListeners []decor.ShutdownListener | |
| 68 | bufP, bufB, bufA, bufE *bytes.Buffer | |
| 69 | panicMsg string | |
| 64 | 70 | |
| 65 | 71 | // following options are assigned to the *Bar |
| 66 | 72 | priority int |
| 103 | 109 | s.bufP = bytes.NewBuffer(make([]byte, 0, width)) |
| 104 | 110 | s.bufB = bytes.NewBuffer(make([]byte, 0, width)) |
| 105 | 111 | s.bufA = bytes.NewBuffer(make([]byte, 0, width)) |
| 106 | if s.newLineExtendFn != nil { | |
| 107 | s.bufNL = bytes.NewBuffer(make([]byte, 0, width)) | |
| 112 | if s.extender != nil { | |
| 113 | s.bufE = bytes.NewBuffer(make([]byte, 0, width)) | |
| 108 | 114 | } |
| 109 | 115 | |
| 110 | 116 | b := &Bar{ |
| 283 | 289 | }() |
| 284 | 290 | r := s.draw(tw) |
| 285 | 291 | var extendedLines int |
| 286 | if s.newLineExtendFn != nil { | |
| 287 | s.bufNL.Reset() | |
| 288 | s.newLineExtendFn(s.bufNL, newStatistics(s)) | |
| 289 | extendedLines = countLines(s.bufNL.Bytes()) | |
| 290 | r = io.MultiReader(r, s.bufNL) | |
| 292 | if s.extender != nil { | |
| 293 | s.extender.Fill(s.bufE, tw, newStatistics(s)) | |
| 294 | extendedLines = countLines(s.bufE.Bytes()) | |
| 295 | r = io.MultiReader(r, s.bufE) | |
| 291 | 296 | } |
| 292 | 297 | b.frameReaderCh <- &frameReader{ |
| 293 | 298 | Reader: r, |
| 301 | 306 | s := b.cacheState |
| 302 | 307 | r := s.draw(tw) |
| 303 | 308 | var extendedLines int |
| 304 | if s.newLineExtendFn != nil { | |
| 305 | s.bufNL.Reset() | |
| 306 | s.newLineExtendFn(s.bufNL, newStatistics(s)) | |
| 307 | extendedLines = countLines(s.bufNL.Bytes()) | |
| 308 | r = io.MultiReader(r, s.bufNL) | |
| 309 | if s.extender != nil { | |
| 310 | s.extender.Fill(s.bufE, tw, newStatistics(s)) | |
| 311 | extendedLines = countLines(s.bufE.Bytes()) | |
| 312 | r = io.MultiReader(r, s.bufE) | |
| 309 | 313 | } |
| 310 | 314 | b.frameReaderCh <- &frameReader{ |
| 311 | 315 | Reader: r, |
| 0 | 0 | package mpb |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | "io" | |
| 4 | 3 | "unicode/utf8" |
| 5 | 4 | |
| 6 | 5 | "github.com/vbauerster/mpb/v4/decor" |
| 93 | 92 | // BarNewLineExtend takes user defined efn, which gets called each |
| 94 | 93 | // render cycle. Any write to provided writer of efn, will appear on |
| 95 | 94 | // new line of respective bar. |
| 96 | func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption { | |
| 95 | func BarExtender(extender Filler) BarOption { | |
| 97 | 96 | return func(s *bState) { |
| 98 | s.newLineExtendFn = efn | |
| 97 | s.extender = extender | |
| 99 | 98 | } |
| 100 | 99 | } |
| 101 | 100 | |