io examples
Vladimir Bauer
9 years ago
| 0 | 0 | package mpb |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | "fmt" | |
| 4 | 3 | "io" |
| 5 | "strconv" | |
| 6 | 4 | "sync" |
| 7 | 5 | "time" |
| 8 | 6 | ) |
| 9 | 7 | |
| 10 | type decoratorFuncType uint | |
| 11 | ||
| 12 | const ( | |
| 13 | decoratorAppend decoratorFuncType = iota | |
| 14 | decoratorPrepend | |
| 15 | ) | |
| 16 | ||
| 17 | // DecoratorFunc is a function that can be prepended and appended to the progress bar | |
| 18 | type DecoratorFunc func(s *Statistics) string | |
| 19 | ||
| 20 | type decorator struct { | |
| 21 | kind decoratorFuncType | |
| 22 | f DecoratorFunc | |
| 23 | } | |
| 24 | ||
| 25 | 8 | // Bar represents a progress Bar |
| 26 | 9 | type Bar struct { |
| 27 | total int | |
| 28 | width int | |
| 29 | alpha float64 | |
| 30 | stopped bool | |
| 10 | total int | |
| 11 | width int | |
| 12 | alpha float64 | |
| 31 | 13 | |
| 32 | 14 | fill byte |
| 33 | 15 | empty byte |
| 35 | 17 | leftEnd byte |
| 36 | 18 | rightEnd byte |
| 37 | 19 | |
| 38 | incrCh chan int | |
| 39 | redrawReqCh chan chan []byte | |
| 40 | statusReqCh chan chan int | |
| 41 | decoratorCh chan *decorator | |
| 42 | flushedCh chan struct{} | |
| 43 | stopCh chan struct{} | |
| 44 | done chan struct{} | |
| 20 | incrCh chan int | |
| 21 | redrawReqCh chan chan []byte | |
| 22 | currentReqCh chan chan int | |
| 23 | statusReqCh chan chan int | |
| 24 | decoratorCh chan *decorator | |
| 25 | flushedCh chan struct{} | |
| 26 | stopCh chan struct{} | |
| 27 | done chan struct{} | |
| 45 | 28 | } |
| 46 | 29 | |
| 47 | 30 | type Statistics struct { |
| 55 | 38 | |
| 56 | 39 | func newBar(total, width int, wg *sync.WaitGroup) *Bar { |
| 57 | 40 | b := &Bar{ |
| 58 | fill: '=', | |
| 59 | empty: '-', | |
| 60 | tip: '>', | |
| 61 | leftEnd: '[', | |
| 62 | rightEnd: ']', | |
| 63 | alpha: 0.25, | |
| 64 | total: total, | |
| 65 | width: width, | |
| 66 | incrCh: make(chan int), | |
| 67 | redrawReqCh: make(chan chan []byte), | |
| 68 | statusReqCh: make(chan chan int), | |
| 69 | decoratorCh: make(chan *decorator), | |
| 70 | flushedCh: make(chan struct{}), | |
| 71 | stopCh: make(chan struct{}), | |
| 72 | done: make(chan struct{}), | |
| 41 | fill: '=', | |
| 42 | empty: '-', | |
| 43 | tip: '>', | |
| 44 | leftEnd: '[', | |
| 45 | rightEnd: ']', | |
| 46 | alpha: 0.25, | |
| 47 | total: total, | |
| 48 | width: width, | |
| 49 | incrCh: make(chan int), | |
| 50 | redrawReqCh: make(chan chan []byte), | |
| 51 | currentReqCh: make(chan chan int), | |
| 52 | statusReqCh: make(chan chan int), | |
| 53 | decoratorCh: make(chan *decorator), | |
| 54 | flushedCh: make(chan struct{}), | |
| 55 | stopCh: make(chan struct{}), | |
| 56 | done: make(chan struct{}), | |
| 73 | 57 | } |
| 74 | 58 | go b.server(wg) |
| 75 | 59 | return b |
| 131 | 115 | return &Reader{r, b} |
| 132 | 116 | } |
| 133 | 117 | |
| 118 | // Incr increments progress bar | |
| 119 | func (b *Bar) Incr(n int) { | |
| 120 | if !b.isDone() { | |
| 121 | b.incrCh <- n | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | // Current returns the actual current. | |
| 126 | // If bar was stopped by Stop(), subsequent calls to Current will return -1 | |
| 127 | func (b *Bar) Current() int { | |
| 128 | if !b.isDone() { | |
| 129 | respCh := make(chan int) | |
| 130 | b.currentReqCh <- respCh | |
| 131 | return <-respCh | |
| 132 | } | |
| 133 | return -1 | |
| 134 | } | |
| 135 | ||
| 136 | // Stop stops rendering the bar | |
| 137 | func (b *Bar) Stop() { | |
| 138 | if !b.isDone() { | |
| 139 | b.stopCh <- struct{}{} | |
| 140 | } | |
| 141 | } | |
| 142 | ||
| 143 | func (b *Bar) InProgress() bool { | |
| 144 | return !b.isDone() | |
| 145 | } | |
| 146 | ||
| 147 | func (b *Bar) PrependFunc(f DecoratorFunc) *Bar { | |
| 148 | b.decoratorCh <- &decorator{decoratorPrepend, f} | |
| 149 | return b | |
| 150 | } | |
| 151 | ||
| 152 | func (b *Bar) AppendFunc(f DecoratorFunc) *Bar { | |
| 153 | b.decoratorCh <- &decorator{decoratorAppend, f} | |
| 154 | return b | |
| 155 | } | |
| 156 | ||
| 134 | 157 | // String returns the string representation of the bar |
| 135 | 158 | func (b *Bar) String() string { |
| 136 | respCh := make(chan []byte) | |
| 137 | b.redrawReqCh <- respCh | |
| 138 | return string(<-respCh) | |
| 139 | } | |
| 140 | ||
| 141 | func (b *Bar) Incr(n int) { | |
| 142 | if !b.isDone() { | |
| 143 | b.incrCh <- n | |
| 144 | } | |
| 145 | } | |
| 146 | ||
| 147 | func (b *Bar) Stop() { | |
| 148 | if !b.stopped { | |
| 149 | b.stopCh <- struct{}{} | |
| 150 | b.stopped = true | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | func (b *Bar) InProgress() bool { | |
| 155 | return !b.isDone() | |
| 156 | } | |
| 157 | ||
| 158 | func (b *Bar) PrependFunc(f DecoratorFunc) *Bar { | |
| 159 | b.decoratorCh <- &decorator{decoratorPrepend, f} | |
| 160 | return b | |
| 161 | } | |
| 162 | ||
| 163 | func (b *Bar) AppendFunc(f DecoratorFunc) *Bar { | |
| 164 | b.decoratorCh <- &decorator{decoratorAppend, f} | |
| 165 | return b | |
| 166 | } | |
| 167 | ||
| 168 | func (b *Bar) PrependName(name string, padding int) *Bar { | |
| 169 | layout := "%" + strconv.Itoa(padding) + "s" | |
| 170 | b.PrependFunc(func(s *Statistics) string { | |
| 171 | return fmt.Sprintf(layout, name) | |
| 172 | }) | |
| 173 | return b | |
| 174 | } | |
| 175 | ||
| 176 | func (b *Bar) PrependETA(padding int) *Bar { | |
| 177 | layout := "ETA%" + strconv.Itoa(padding) + "s" | |
| 178 | b.PrependFunc(func(s *Statistics) string { | |
| 179 | return fmt.Sprintf(layout, time.Duration(s.eta().Seconds())*time.Second) | |
| 180 | }) | |
| 181 | return b | |
| 182 | } | |
| 183 | ||
| 184 | func (b *Bar) AppendETA() *Bar { | |
| 185 | b.AppendFunc(func(s *Statistics) string { | |
| 186 | return fmt.Sprintf("ETA %s", time.Duration(s.eta().Seconds())*time.Second) | |
| 187 | }) | |
| 188 | return b | |
| 189 | } | |
| 190 | ||
| 191 | func (b *Bar) PrependElapsed(padding int) *Bar { | |
| 192 | layout := "%" + strconv.Itoa(padding) + "s" | |
| 193 | b.PrependFunc(func(s *Statistics) string { | |
| 194 | return fmt.Sprintf(layout, time.Duration(s.TimeElapsed.Seconds())*time.Second) | |
| 195 | }) | |
| 196 | return b | |
| 197 | } | |
| 198 | ||
| 199 | func (b *Bar) AppendElapsed() *Bar { | |
| 200 | b.AppendFunc(func(s *Statistics) string { | |
| 201 | return fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second) | |
| 202 | }) | |
| 203 | return b | |
| 204 | } | |
| 205 | ||
| 206 | func (b *Bar) AppendPercentage() *Bar { | |
| 207 | b.AppendFunc(func(s *Statistics) string { | |
| 208 | completed := int(100 * float64(s.Current) / float64(s.Total)) | |
| 209 | return fmt.Sprintf("%3d %%", completed) | |
| 210 | }) | |
| 211 | return b | |
| 212 | } | |
| 213 | ||
| 214 | func (b *Bar) PrependPercentage(padding int) *Bar { | |
| 215 | layout := "%" + strconv.Itoa(padding) + "d %%" | |
| 216 | b.PrependFunc(func(s *Statistics) string { | |
| 217 | completed := int(100 * float64(s.Current) / float64(s.Total)) | |
| 218 | return fmt.Sprintf(layout, completed) | |
| 219 | }) | |
| 220 | return b | |
| 159 | if !b.isDone() { | |
| 160 | respCh := make(chan []byte) | |
| 161 | b.redrawReqCh <- respCh | |
| 162 | return string(<-respCh) | |
| 163 | } | |
| 164 | return "" | |
| 221 | 165 | } |
| 222 | 166 | |
| 223 | 167 | func (b *Bar) server(wg *sync.WaitGroup) { |
| 253 | 197 | case decoratorPrepend: |
| 254 | 198 | prependFuncs = append(prependFuncs, d.f) |
| 255 | 199 | } |
| 200 | case respCh := <-b.currentReqCh: | |
| 201 | respCh <- current | |
| 256 | 202 | case respCh := <-b.redrawReqCh: |
| 257 | 203 | stat := &Statistics{b.total, current, timeElapsed, tpie} |
| 258 | 204 | respCh <- b.draw(stat, buf, appendFuncs, prependFuncs) |
| 264 | 210 | wg.Done() |
| 265 | 211 | } |
| 266 | 212 | case <-b.stopCh: |
| 213 | close(b.done) | |
| 267 | 214 | if !completed { |
| 268 | close(b.done) | |
| 269 | 215 | wg.Done() |
| 270 | 216 | } |
| 271 | 217 | return |
| 0 | package mpb | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "strconv" | |
| 5 | "time" | |
| 6 | ) | |
| 7 | ||
| 8 | type decoratorFuncType uint | |
| 9 | ||
| 10 | const ( | |
| 11 | decoratorAppend decoratorFuncType = iota | |
| 12 | decoratorPrepend | |
| 13 | ) | |
| 14 | ||
| 15 | // DecoratorFunc is a function that can be prepended and appended to the progress bar | |
| 16 | type DecoratorFunc func(s *Statistics) string | |
| 17 | ||
| 18 | type decorator struct { | |
| 19 | kind decoratorFuncType | |
| 20 | f DecoratorFunc | |
| 21 | } | |
| 22 | ||
| 23 | func (b *Bar) PrependName(name string, padding int) *Bar { | |
| 24 | layout := "%" + strconv.Itoa(padding) + "s" | |
| 25 | b.PrependFunc(func(s *Statistics) string { | |
| 26 | return fmt.Sprintf(layout, name) | |
| 27 | }) | |
| 28 | return b | |
| 29 | } | |
| 30 | ||
| 31 | func (b *Bar) PrependCounters(unit Units, padding int) *Bar { | |
| 32 | layout := "%" + strconv.Itoa(padding) + "s" | |
| 33 | b.PrependFunc(func(s *Statistics) string { | |
| 34 | current := Format(s.Current).To(unit) | |
| 35 | total := Format(s.Total).To(unit) | |
| 36 | str := fmt.Sprintf("%s / %s", current, total) | |
| 37 | return fmt.Sprintf(layout, str) | |
| 38 | }) | |
| 39 | return b | |
| 40 | } | |
| 41 | ||
| 42 | func (b *Bar) PrependETA(padding int) *Bar { | |
| 43 | layout := "ETA%" + strconv.Itoa(padding) + "s" | |
| 44 | b.PrependFunc(func(s *Statistics) string { | |
| 45 | return fmt.Sprintf(layout, time.Duration(s.eta().Seconds())*time.Second) | |
| 46 | }) | |
| 47 | return b | |
| 48 | } | |
| 49 | ||
| 50 | func (b *Bar) AppendETA() *Bar { | |
| 51 | b.AppendFunc(func(s *Statistics) string { | |
| 52 | return fmt.Sprintf("ETA %s", time.Duration(s.eta().Seconds())*time.Second) | |
| 53 | }) | |
| 54 | return b | |
| 55 | } | |
| 56 | ||
| 57 | func (b *Bar) PrependElapsed(padding int) *Bar { | |
| 58 | layout := "%" + strconv.Itoa(padding) + "s" | |
| 59 | b.PrependFunc(func(s *Statistics) string { | |
| 60 | return fmt.Sprintf(layout, time.Duration(s.TimeElapsed.Seconds())*time.Second) | |
| 61 | }) | |
| 62 | return b | |
| 63 | } | |
| 64 | ||
| 65 | func (b *Bar) AppendElapsed() *Bar { | |
| 66 | b.AppendFunc(func(s *Statistics) string { | |
| 67 | return fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second) | |
| 68 | }) | |
| 69 | return b | |
| 70 | } | |
| 71 | ||
| 72 | func (b *Bar) AppendPercentage() *Bar { | |
| 73 | b.AppendFunc(func(s *Statistics) string { | |
| 74 | completed := int(100 * float64(s.Current) / float64(s.Total)) | |
| 75 | return fmt.Sprintf("%3d %%", completed) | |
| 76 | }) | |
| 77 | return b | |
| 78 | } | |
| 79 | ||
| 80 | func (b *Bar) PrependPercentage(padding int) *Bar { | |
| 81 | layout := "%" + strconv.Itoa(padding) + "d %%" | |
| 82 | b.PrependFunc(func(s *Statistics) string { | |
| 83 | completed := int(100 * float64(s.Current) / float64(s.Total)) | |
| 84 | return fmt.Sprintf(layout, completed) | |
| 85 | }) | |
| 86 | return b | |
| 87 | } |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | p := mpb.New() |
| 22 | p.Wg.Add(1) | |
| 22 | 23 | bar := p.AddBar(totalItem).AppendETA().PrependFunc(decor) |
| 23 | 24 | |
| 24 | 25 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "io" | |
| 5 | "log" | |
| 6 | "net/http" | |
| 7 | "os" | |
| 8 | "path/filepath" | |
| 9 | ||
| 10 | "github.com/vbauerster/mpb" | |
| 11 | ) | |
| 12 | ||
| 13 | func main() { | |
| 14 | log.SetOutput(os.Stderr) | |
| 15 | ||
| 16 | url1 := "https://homebrew.bintray.com/bottles/youtube-dl-2016.12.12.sierra.bottle.tar.gz" | |
| 17 | url2 := "https://homebrew.bintray.com/bottles/libtiff-4.0.7.sierra.bottle.tar.gz" | |
| 18 | ||
| 19 | p := mpb.New().SetWidth(60) | |
| 20 | ||
| 21 | for i, url := range [...]string{url1, url2} { | |
| 22 | p.Wg.Add(1) // if you omit this line, main will return without waiting for download goroutines | |
| 23 | name := fmt.Sprintf("url%d:", i+1) | |
| 24 | go download(p, name, url) | |
| 25 | } | |
| 26 | ||
| 27 | p.WaitAndStop() | |
| 28 | fmt.Println("Finished") | |
| 29 | } | |
| 30 | ||
| 31 | func download(p *mpb.Progress, name, url string) { | |
| 32 | resp, err := http.Get(url) | |
| 33 | if err != nil { | |
| 34 | log.Printf("%s: %v", name, err) | |
| 35 | return | |
| 36 | } | |
| 37 | defer resp.Body.Close() | |
| 38 | ||
| 39 | if resp.StatusCode != http.StatusOK { | |
| 40 | err = fmt.Errorf("non-200 status: %s", resp.Status) | |
| 41 | log.Printf("%s: %v", name, err) | |
| 42 | return | |
| 43 | } | |
| 44 | ||
| 45 | size := resp.ContentLength | |
| 46 | ||
| 47 | // create dest | |
| 48 | destName := filepath.Base(url) | |
| 49 | dest, err := os.Create(destName) | |
| 50 | if err != nil { | |
| 51 | err = fmt.Errorf("Can't create %s: %v", destName, err) | |
| 52 | log.Printf("%s: %v", name, err) | |
| 53 | return | |
| 54 | } | |
| 55 | ||
| 56 | // create bar with appropriate decorators | |
| 57 | bar := p.AddBar(int(size)). | |
| 58 | PrependCounters(mpb.UnitBytes, 19). | |
| 59 | PrependName(name, len(name)). | |
| 60 | AppendETA() | |
| 61 | // create proxy reader | |
| 62 | reader := bar.ProxyReader(resp.Body) | |
| 63 | // and copy from reader | |
| 64 | _, err = io.Copy(dest, reader) | |
| 65 | ||
| 66 | if closeErr := dest.Close(); err == nil { | |
| 67 | err = closeErr | |
| 68 | } | |
| 69 | if err != nil { | |
| 70 | log.Printf("%s: %v", name, err) | |
| 71 | } | |
| 72 | } |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "io" | |
| 5 | "net/http" | |
| 6 | "os" | |
| 7 | "path/filepath" | |
| 8 | ||
| 9 | "github.com/vbauerster/mpb" | |
| 10 | ) | |
| 11 | ||
| 12 | func main() { | |
| 13 | url := "https://homebrew.bintray.com/bottles/libtiff-4.0.7.sierra.bottle.tar.gz" | |
| 14 | ||
| 15 | resp, err := http.Get(url) | |
| 16 | if err != nil { | |
| 17 | panic(err) | |
| 18 | } | |
| 19 | defer resp.Body.Close() | |
| 20 | ||
| 21 | if resp.StatusCode != http.StatusOK { | |
| 22 | fmt.Printf("Server return non-200 status: %s\n", resp.Status) | |
| 23 | return | |
| 24 | } | |
| 25 | ||
| 26 | size := resp.ContentLength | |
| 27 | ||
| 28 | // create dest | |
| 29 | destName := filepath.Base(url) | |
| 30 | dest, err := os.Create(destName) | |
| 31 | if err != nil { | |
| 32 | fmt.Printf("Can't create %s: %v\n", destName, err) | |
| 33 | return | |
| 34 | } | |
| 35 | defer dest.Close() | |
| 36 | ||
| 37 | p := mpb.New().SetWidth(64) | |
| 38 | // if you omit following line, download will complete fine, but rendering bar | |
| 39 | // may not complete, thus better always use even in single thread. | |
| 40 | p.Wg.Add(1) | |
| 41 | ||
| 42 | bar := p.AddBar(int(size)).PrependCounters(mpb.UnitBytes, 19).AppendETA() | |
| 43 | ||
| 44 | // create proxy reader | |
| 45 | reader := bar.ProxyReader(resp.Body) | |
| 46 | ||
| 47 | // and copy from reader, ignoring errors | |
| 48 | io.Copy(dest, reader) | |
| 49 | ||
| 50 | p.WaitAndStop() | |
| 51 | fmt.Println("Finished") | |
| 52 | } |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "math/rand" | |
| 5 | "time" | |
| 6 | ||
| 7 | "github.com/vbauerster/mpb" | |
| 8 | ) | |
| 9 | ||
| 10 | const ( | |
| 11 | maxBlockSize = 12 | |
| 12 | ) | |
| 13 | ||
| 14 | func main() { | |
| 15 | decor := func(s *mpb.Statistics) string { | |
| 16 | str := fmt.Sprintf("%d/%d", s.Current, s.Total) | |
| 17 | return fmt.Sprintf("%-7s", str) | |
| 18 | } | |
| 19 | ||
| 20 | p := mpb.New().RefreshRate(80 * time.Millisecond) | |
| 21 | ||
| 22 | bar1 := p.AddBar(50).AppendETA().PrependFunc(decor) | |
| 23 | go func() { | |
| 24 | blockSize := rand.Intn(maxBlockSize) + 1 | |
| 25 | for i := 0; i < 50; i++ { | |
| 26 | time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) | |
| 27 | bar1.Incr(1) | |
| 28 | blockSize = rand.Intn(maxBlockSize) + 1 | |
| 29 | } | |
| 30 | }() | |
| 31 | ||
| 32 | bar2 := p.AddBar(100).AppendETA().PrependFunc(decor) | |
| 33 | go func() { | |
| 34 | blockSize := rand.Intn(maxBlockSize) + 1 | |
| 35 | for i := 0; i < 100; i++ { | |
| 36 | time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) | |
| 37 | bar2.Incr(1) | |
| 38 | blockSize = rand.Intn(maxBlockSize) + 1 | |
| 39 | } | |
| 40 | }() | |
| 41 | ||
| 42 | bar3 := p.AddBar(80).AppendETA().PrependFunc(decor) | |
| 43 | go func() { | |
| 44 | blockSize := rand.Intn(maxBlockSize) + 1 | |
| 45 | for i := 0; i < 80; i++ { | |
| 46 | time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) | |
| 47 | bar3.Incr(1) | |
| 48 | blockSize = rand.Intn(maxBlockSize) + 1 | |
| 49 | } | |
| 50 | }() | |
| 51 | ||
| 52 | // time.Sleep(time.Second) | |
| 53 | // p.RemoveBar(bar2) | |
| 54 | ||
| 55 | p.WaitAndStop() | |
| 56 | bar2.Incr(2) | |
| 57 | fmt.Println("stop") | |
| 58 | // p.AddBar(1) // panic: send on closed channnel | |
| 59 | } |
| 13 | 13 | |
| 14 | 14 | func main() { |
| 15 | 15 | |
| 16 | p := mpb.New().SetWidth(64) | |
| 16 | 17 | // p := mpb.New().RefreshRate(80 * time.Millisecond).SetWidth(64) |
| 17 | p := mpb.New().SetWidth(64) | |
| 18 | 18 | |
| 19 | 19 | name1 := "Bar#1:" |
| 20 | 20 | bar1 := p.AddBar(50).AppendPercentage().PrependETA(4).PrependName(name1, len(name1)) |
| 21 | p.Wg.Add(1) | |
| 21 | 22 | go func() { |
| 22 | 23 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 23 | 24 | for i := 0; i < 50; i++ { |
| 28 | 29 | }() |
| 29 | 30 | |
| 30 | 31 | bar2 := p.AddBar(100).AppendPercentage().PrependETA(4).PrependName("", 0-len(name1)) |
| 32 | p.Wg.Add(1) | |
| 31 | 33 | go func() { |
| 32 | 34 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 33 | 35 | for i := 0; i < 100; i++ { |
| 38 | 40 | }() |
| 39 | 41 | |
| 40 | 42 | bar3 := p.AddBar(80).AppendPercentage().PrependETA(4).PrependName("Bar#3:", 0) |
| 43 | p.Wg.Add(1) | |
| 41 | 44 | go func() { |
| 42 | 45 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 43 | 46 | for i := 0; i < 80; i++ { |
| 14 | 14 | func main() { |
| 15 | 15 | |
| 16 | 16 | p := mpb.New().SetWidth(64) |
| 17 | // p := mpb.New().RefreshRate(80 * time.Millisecond).SetWidth(64) | |
| 17 | 18 | |
| 18 | 19 | name1 := "Bar#1:" |
| 19 | 20 | bar1 := p.AddBar(50).AppendPercentage().PrependElapsed(3).PrependName(name1, len(name1)) |
| 21 | p.Wg.Add(1) | |
| 20 | 22 | go func() { |
| 21 | 23 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 22 | 24 | for i := 0; i < 50; i++ { |
| 27 | 29 | }() |
| 28 | 30 | |
| 29 | 31 | bar2 := p.AddBar(100).AppendPercentage().PrependElapsed(3).PrependName("", 0-len(name1)) |
| 32 | p.Wg.Add(1) | |
| 30 | 33 | go func() { |
| 31 | 34 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 32 | 35 | for i := 0; i < 100; i++ { |
| 37 | 40 | }() |
| 38 | 41 | |
| 39 | 42 | bar3 := p.AddBar(80).AppendPercentage().PrependElapsed(3).PrependName("Bar#3:", 0) |
| 43 | p.Wg.Add(1) | |
| 40 | 44 | go func() { |
| 41 | 45 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 42 | 46 | for i := 0; i < 80; i++ { |
| 13 | 13 | |
| 14 | 14 | func main() { |
| 15 | 15 | |
| 16 | p := mpb.New().SetWidth(64) | |
| 16 | 17 | // p := mpb.New().RefreshRate(100 * time.Millisecond).SetWidth(64) |
| 17 | p := mpb.New().SetWidth(64) | |
| 18 | 18 | |
| 19 | 19 | name1 := "Bar#1:" |
| 20 | 20 | bar1 := p.AddBar(50).AppendETA().PrependPercentage(3).PrependName(name1, len(name1)) |
| 21 | p.Wg.Add(1) | |
| 21 | 22 | go func() { |
| 22 | 23 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 23 | 24 | for i := 0; i < 50; i++ { |
| 28 | 29 | }() |
| 29 | 30 | |
| 30 | 31 | bar2 := p.AddBar(100).AppendETA().PrependPercentage(3).PrependName("", 0-len(name1)) |
| 32 | p.Wg.Add(1) | |
| 31 | 33 | go func() { |
| 32 | 34 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 33 | 35 | for i := 0; i < 100; i++ { |
| 38 | 40 | }() |
| 39 | 41 | |
| 40 | 42 | bar3 := p.AddBar(80).AppendETA().PrependPercentage(3).PrependName("Bar#3:", 0) |
| 43 | p.Wg.Add(1) | |
| 41 | 44 | go func() { |
| 42 | 45 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 43 | 46 | for i := 0; i < 80; i++ { |
| 2 | 2 | import ( |
| 3 | 3 | "fmt" |
| 4 | 4 | "math/rand" |
| 5 | "runtime" | |
| 6 | 5 | "time" |
| 7 | 6 | |
| 8 | "github.com/vbauerster/uiprogress" | |
| 7 | "github.com/vbauerster/mpb" | |
| 9 | 8 | ) |
| 10 | 9 | |
| 11 | 10 | const ( |
| 12 | 11 | totalItem = 100 |
| 13 | maxBlockSize = 20 | |
| 12 | maxBlockSize = 14 | |
| 14 | 13 | ) |
| 15 | 14 | |
| 16 | 15 | func main() { |
| 17 | runtime.GOMAXPROCS(runtime.NumCPU()) | |
| 18 | decor := func(s *uiprogress.Statistics) string { | |
| 19 | str := fmt.Sprintf("%d/%d", s.Completed, s.Total) | |
| 16 | decor := func(s *mpb.Statistics) string { | |
| 17 | str := fmt.Sprintf("%d/%d", s.Current, s.Total) | |
| 20 | 18 | return fmt.Sprintf("%-7s", str) |
| 21 | 19 | } |
| 22 | 20 | |
| 23 | p := uiprogress.New() | |
| 21 | p := mpb.New() | |
| 24 | 22 | bar := p.AddBar(totalItem).AppendETA().PrependFunc(decor) |
| 23 | p.Wg.Add(1) | |
| 25 | 24 | |
| 26 | 25 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 27 | for i := 0; !bar.IsCompleted(); i += 1 { | |
| 26 | for i := 0; bar.InProgress(); i++ { | |
| 28 | 27 | time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) |
| 29 | bar.Incr(1) | |
| 30 | if i == 42 { | |
| 28 | bar.Incr(blockSize) | |
| 29 | if bar.Current() > 42 { | |
| 31 | 30 | p.RemoveBar(bar) |
| 32 | 31 | } |
| 33 | 32 | blockSize = rand.Intn(maxBlockSize) + 1 |
| 17 | 17 | |
| 18 | 18 | name1 := "Bar#1:" |
| 19 | 19 | bar1 := p.AddBar(100).AppendETA().PrependFunc(getDecor()).PrependName(name1, len(name1)) |
| 20 | p.Wg.Add(1) | |
| 20 | 21 | go func() { |
| 21 | 22 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 22 | 23 | for i := 0; i < 100; i++ { |
| 27 | 28 | }() |
| 28 | 29 | |
| 29 | 30 | bar2 := p.AddBar(60).AppendETA().PrependFunc(getDecor()).PrependName("", 0-len(name1)) |
| 31 | p.Wg.Add(1) | |
| 30 | 32 | go func() { |
| 31 | 33 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 32 | 34 | for i := 0; i < 60; i++ { |
| 37 | 39 | }() |
| 38 | 40 | |
| 39 | 41 | bar3 := p.AddBar(80).AppendETA().PrependFunc(getDecor()).PrependName("Bar#3:", 0) |
| 42 | p.Wg.Add(1) | |
| 40 | 43 | go func() { |
| 41 | 44 | blockSize := rand.Intn(maxBlockSize) + 1 |
| 42 | 45 | for i := 0; i < 80; i++ { |
| 0 | package mpb | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "strings" | |
| 5 | ) | |
| 6 | ||
| 7 | const ( | |
| 8 | _ = iota | |
| 9 | bytesInKiB = 1 << (iota * 10) | |
| 10 | bytesInMiB | |
| 11 | bytesInGiB | |
| 12 | bytesInTiB | |
| 13 | ) | |
| 14 | ||
| 15 | type Units uint | |
| 16 | ||
| 17 | const ( | |
| 18 | UnitNone Units = iota | |
| 19 | UnitBytes | |
| 20 | ) | |
| 21 | ||
| 22 | func Format(i int) *formatter { | |
| 23 | return &formatter{n: i} | |
| 24 | } | |
| 25 | ||
| 26 | type formatter struct { | |
| 27 | n int | |
| 28 | unit Units | |
| 29 | width int | |
| 30 | } | |
| 31 | ||
| 32 | func (f *formatter) To(unit Units) *formatter { | |
| 33 | f.unit = unit | |
| 34 | return f | |
| 35 | } | |
| 36 | ||
| 37 | func (f *formatter) Width(width int) *formatter { | |
| 38 | f.width = width | |
| 39 | return f | |
| 40 | } | |
| 41 | ||
| 42 | func (f *formatter) String() string { | |
| 43 | switch f.unit { | |
| 44 | case UnitBytes: | |
| 45 | return formatBytes(f.n) | |
| 46 | default: | |
| 47 | return fmt.Sprintf(fmt.Sprintf("%%%dd", f.width), f.n) | |
| 48 | } | |
| 49 | } | |
| 50 | ||
| 51 | func formatBytes(i int) (result string) { | |
| 52 | switch { | |
| 53 | case i > bytesInTiB: | |
| 54 | result = fmt.Sprintf("%.02fTiB", float64(i)/bytesInTiB) | |
| 55 | case i > bytesInGiB: | |
| 56 | result = fmt.Sprintf("%.02fGiB", float64(i)/bytesInGiB) | |
| 57 | case i > bytesInMiB: | |
| 58 | result = fmt.Sprintf("%.02fMiB", float64(i)/bytesInMiB) | |
| 59 | case i > bytesInKiB: | |
| 60 | result = fmt.Sprintf("%.02fKiB", float64(i)/bytesInKiB) | |
| 61 | default: | |
| 62 | result = fmt.Sprintf("%db", i) | |
| 63 | } | |
| 64 | result = strings.Trim(result, " ") | |
| 65 | return | |
| 66 | } |
| 30 | 30 | |
| 31 | 31 | // Progress represents the container that renders Progress bars |
| 32 | 32 | type Progress struct { |
| 33 | out io.Writer | |
| 34 | width int | |
| 35 | sort SortType | |
| 36 | stopped bool | |
| 33 | Wg *sync.WaitGroup | |
| 34 | ||
| 35 | out io.Writer | |
| 36 | width int | |
| 37 | sort SortType | |
| 38 | // stopped bool | |
| 37 | 39 | |
| 38 | 40 | op chan *operation |
| 39 | 41 | rrChangeReqCh chan time.Duration |
| 40 | 42 | outChangeReqCh chan io.Writer |
| 41 | 43 | countReqCh chan chan int |
| 42 | ||
| 43 | wg *sync.WaitGroup | |
| 44 | allDone chan struct{} | |
| 44 | 45 | } |
| 45 | 46 | |
| 46 | 47 | type operation struct { |
| 57 | 58 | rrChangeReqCh: make(chan time.Duration), |
| 58 | 59 | outChangeReqCh: make(chan io.Writer), |
| 59 | 60 | countReqCh: make(chan chan int), |
| 60 | wg: new(sync.WaitGroup), | |
| 61 | allDone: make(chan struct{}), | |
| 62 | Wg: new(sync.WaitGroup), | |
| 61 | 63 | } |
| 62 | 64 | go p.server(cwriter.New(os.Stdout), time.NewTicker(rr*time.Millisecond)) |
| 63 | 65 | return p |
| 64 | 66 | } |
| 65 | 67 | |
| 68 | // SetWidth sets the width for all underlying bars | |
| 66 | 69 | func (p *Progress) SetWidth(n int) *Progress { |
| 67 | 70 | if n <= 0 { |
| 68 | 71 | return p |
| 87 | 90 | return p |
| 88 | 91 | } |
| 89 | 92 | |
| 93 | // WithSort sorts the bars, while redering | |
| 90 | 94 | func (p *Progress) WithSort(sort SortType) *Progress { |
| 91 | 95 | p.sort = sort |
| 92 | 96 | return p |
| 94 | 98 | |
| 95 | 99 | // AddBar creates a new progress bar and adds to the container |
| 96 | 100 | func (p *Progress) AddBar(total int) *Bar { |
| 97 | p.wg.Add(1) | |
| 98 | bar := newBar(total, p.width, p.wg) | |
| 101 | bar := newBar(total, p.width, p.Wg) | |
| 99 | 102 | p.op <- &operation{opBarAdd, bar, nil} |
| 100 | 103 | return bar |
| 101 | 104 | } |
| 102 | 105 | |
| 106 | // RemoveBar removes bar at any time | |
| 103 | 107 | func (p *Progress) RemoveBar(b *Bar) bool { |
| 104 | 108 | result := make(chan bool) |
| 105 | 109 | p.op <- &operation{opBarRemove, b, result} |
| 106 | 110 | return <-result |
| 107 | 111 | } |
| 108 | 112 | |
| 113 | // BarsCount returns bars count in the container | |
| 109 | 114 | func (p *Progress) BarsCount() int { |
| 110 | 115 | respCh := make(chan int) |
| 111 | 116 | p.countReqCh <- respCh |
| 114 | 119 | |
| 115 | 120 | // WaitAndStop stops listening |
| 116 | 121 | func (p *Progress) WaitAndStop() { |
| 117 | if !p.stopped { | |
| 118 | // fmt.Fprintln(os.Stderr, "p.WaitAndStop") | |
| 119 | p.stopped = true | |
| 120 | p.wg.Wait() | |
| 122 | if !p.isAllDone() { | |
| 123 | close(p.allDone) | |
| 124 | p.Wg.Wait() | |
| 121 | 125 | close(p.op) |
| 122 | 126 | } |
| 123 | 127 | } |
| 132 | 136 | cw = cwriter.New(w) |
| 133 | 137 | case op, ok := <-p.op: |
| 134 | 138 | if !ok { |
| 135 | // fmt.Fprintln(os.Stderr, "Sopping bars") | |
| 136 | 139 | for _, b := range bars { |
| 137 | 140 | b.Stop() |
| 138 | 141 | } |
| 179 | 182 | } |
| 180 | 183 | } |
| 181 | 184 | } |
| 185 | ||
| 186 | func (p *Progress) isAllDone() bool { | |
| 187 | select { | |
| 188 | case <-p.allDone: | |
| 189 | return true | |
| 190 | default: | |
| 191 | return false | |
| 192 | } | |
| 193 | } | |