introduce left, right bar space
Vladimir Bauer
9 years ago
| 17 | 17 | leftEnd byte |
| 18 | 18 | rightEnd byte |
| 19 | 19 | |
| 20 | lastFrame []byte | |
| 21 | lastStatus int | |
| 20 | trimLeftSpace, trimRightSpace bool | |
| 22 | 21 | |
| 23 | 22 | incrCh chan int |
| 24 | 23 | redrawReqCh chan *redrawRequest |
| 37 | 36 | TimeElapsed, TimePerItemEstimate time.Duration |
| 38 | 37 | } |
| 39 | 38 | |
| 40 | func (s *Statistics) eta() time.Duration { | |
| 39 | func (s *Statistics) Eta() time.Duration { | |
| 41 | 40 | return time.Duration(s.Total-s.Current) * s.TimePerItemEstimate |
| 42 | 41 | } |
| 43 | 42 | |
| 66 | 65 | |
| 67 | 66 | // SetWidth sets width of the bar |
| 68 | 67 | func (b *Bar) SetWidth(n int) *Bar { |
| 69 | if n <= 0 { | |
| 68 | if n < 2 { | |
| 70 | 69 | return b |
| 71 | 70 | } |
| 72 | 71 | b.width = n |
| 72 | return b | |
| 73 | } | |
| 74 | ||
| 75 | func (b *Bar) TrimLeftSpace() *Bar { | |
| 76 | b.trimLeftSpace = true | |
| 77 | return b | |
| 78 | } | |
| 79 | ||
| 80 | func (b *Bar) TrimRightSpace() *Bar { | |
| 81 | b.trimRightSpace = true | |
| 73 | 82 | return b |
| 74 | 83 | } |
| 75 | 84 | |
| 128 | 137 | } |
| 129 | 138 | |
| 130 | 139 | // Current returns the actual current. |
| 131 | // If bar was stopped by Stop(), subsequent calls to Current will return -1 | |
| 132 | 140 | func (b *Bar) Current() int { |
| 133 | if b.isDone() { | |
| 134 | return -1 | |
| 135 | } | |
| 136 | 141 | respCh := make(chan int) |
| 137 | 142 | b.currentReqCh <- respCh |
| 138 | 143 | return <-respCh |
| 163 | 168 | return b |
| 164 | 169 | } |
| 165 | 170 | |
| 166 | // String returns the string representation of the bar | |
| 167 | // func (b *Bar) String() string { | |
| 168 | // if b.isDone() { | |
| 169 | // return string(b.lastFrame) | |
| 170 | // } | |
| 171 | // respCh := make(chan []byte) | |
| 172 | // b.redrawReqCh <- respCh | |
| 173 | // return string(<-respCh) | |
| 174 | // } | |
| 175 | ||
| 176 | 171 | type redrawRequest struct { |
| 177 | 172 | width int |
| 178 | 173 | respCh chan []byte |
| 179 | 174 | } |
| 180 | 175 | |
| 181 | 176 | func (b *Bar) Bytes(width int) []byte { |
| 182 | if b.isDone() { | |
| 183 | return b.lastFrame | |
| 177 | if width <= 0 { | |
| 178 | width = b.width | |
| 184 | 179 | } |
| 185 | 180 | respCh := make(chan []byte) |
| 186 | 181 | b.redrawReqCh <- &redrawRequest{width, respCh} |
| 190 | 185 | func (b *Bar) server(wg *sync.WaitGroup, total int) { |
| 191 | 186 | timeStarted := time.Now() |
| 192 | 187 | blockStartTime := timeStarted |
| 193 | // buf := make([]byte, 0, b.width+32) | |
| 194 | 188 | var tpie time.Duration |
| 195 | 189 | var timeElapsed time.Duration |
| 196 | 190 | var appendFuncs []DecoratorFunc |
| 231 | 225 | respCh <- percentage(total, current, 100) |
| 232 | 226 | case <-b.flushedCh: |
| 233 | 227 | if completed && !b.isDone() { |
| 234 | stat := &Statistics{total, current, termWidth, timeElapsed, tpie} | |
| 235 | b.lastFrame = b.draw(stat, appendFuncs, prependFuncs) | |
| 236 | b.lastStatus = percentage(total, current, 100) | |
| 237 | 228 | close(b.done) |
| 238 | 229 | wg.Done() |
| 239 | return | |
| 240 | 230 | } |
| 241 | 231 | case <-b.stopCh: |
| 242 | 232 | close(b.done) |
| 243 | 233 | if !completed { |
| 244 | 234 | wg.Done() |
| 245 | 235 | } |
| 246 | return | |
| 247 | 236 | } |
| 248 | 237 | } |
| 249 | 238 | } |
| 269 | 258 | prependCount := utf8.RuneCount(prependBlock) |
| 270 | 259 | barCount := utf8.RuneCount(barBlock) |
| 271 | 260 | appendCount := utf8.RuneCount(appendBlock) |
| 261 | ||
| 262 | var leftSpace, rightSpace []byte | |
| 263 | space := []byte{' '} | |
| 264 | if !b.trimLeftSpace { | |
| 265 | prependCount++ | |
| 266 | leftSpace = space | |
| 267 | } | |
| 268 | if !b.trimRightSpace { | |
| 269 | appendCount++ | |
| 270 | rightSpace = space | |
| 271 | } | |
| 272 | ||
| 272 | 273 | totalCount := prependCount + barCount + appendCount |
| 273 | ||
| 274 | 274 | if totalCount >= stat.TermWidth { |
| 275 | 275 | newWidth := stat.TermWidth - prependCount - appendCount |
| 276 | 276 | barBlock = b.fillBar(stat.Total, stat.Current, newWidth-1) |
| 277 | 277 | } |
| 278 | 278 | |
| 279 | for _, block := range [...][]byte{prependBlock, barBlock, appendBlock} { | |
| 279 | for _, block := range [...][]byte{prependBlock, leftSpace, barBlock, rightSpace, appendBlock} { | |
| 280 | 280 | buf = append(buf, block...) |
| 281 | 281 | } |
| 282 | 282 | |
| 287 | 287 | if width < 2 { |
| 288 | 288 | return []byte{b.leftEnd, b.rightEnd} |
| 289 | 289 | } |
| 290 | ||
| 290 | 291 | buf := make([]byte, width) |
| 291 | 292 | completedWidth := percentage(total, current, width) |
| 292 | 293 | |
| 302 | 303 | } |
| 303 | 304 | // set left and right ends bits |
| 304 | 305 | buf[0], buf[width-1] = b.leftEnd, b.rightEnd |
| 306 | ||
| 305 | 307 | return buf |
| 306 | 308 | } |
| 307 | 309 | |
| 315 | 317 | } |
| 316 | 318 | |
| 317 | 319 | func (b *Bar) status() int { |
| 318 | if b.isDone() { | |
| 319 | return b.lastStatus | |
| 320 | } | |
| 321 | 320 | respCh := make(chan int) |
| 322 | 321 | b.statusReqCh <- respCh |
| 323 | 322 | return <-respCh |
| 29 | 29 | func TerminalWidth() (int, error) { |
| 30 | 30 | w := new(window) |
| 31 | 31 | tio := syscall.TIOCGWINSZ |
| 32 | res, _, err := syscall.Syscall(syscall.SYS_IOCTL, | |
| 32 | _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, | |
| 33 | 33 | tty.Fd(), |
| 34 | 34 | uintptr(tio), |
| 35 | 35 | uintptr(unsafe.Pointer(w)), |
| 36 | 36 | ) |
| 37 | if int(res) == -1 { | |
| 38 | return 0, err | |
| 37 | if errno != 0 { | |
| 38 | return 0, errno | |
| 39 | 39 | } |
| 40 | 40 | return int(w.Col), nil |
| 41 | 41 | } |
| 42 | 42 | func (b *Bar) PrependETA(padding int) *Bar { |
| 43 | 43 | layout := "ETA%" + strconv.Itoa(padding) + "s" |
| 44 | 44 | b.PrependFunc(func(s *Statistics) string { |
| 45 | return fmt.Sprintf(layout, time.Duration(s.eta().Seconds())*time.Second) | |
| 45 | return fmt.Sprintf(layout, time.Duration(s.Eta().Seconds())*time.Second) | |
| 46 | 46 | }) |
| 47 | 47 | return b |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | 50 | func (b *Bar) AppendETA() *Bar { |
| 51 | 51 | b.AppendFunc(func(s *Statistics) string { |
| 52 | return fmt.Sprintf("ETA %s", time.Duration(s.eta().Seconds())*time.Second) | |
| 52 | return fmt.Sprintf("ETA %s", time.Duration(s.Eta().Seconds())*time.Second) | |
| 53 | 53 | }) |
| 54 | 54 | return b |
| 55 | 55 | } |
| 25 | 25 | for i := 0; bar.InProgress(); i++ { |
| 26 | 26 | time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) |
| 27 | 27 | bar.Incr(blockSize) |
| 28 | if bar.Current() > 42 { | |
| 29 | p.RemoveBar(bar) | |
| 28 | if bar.Current() > 42 && p.RemoveBar(bar) { | |
| 29 | break | |
| 30 | 30 | } |
| 31 | 31 | blockSize = rand.Intn(maxBlockSize) + 1 |
| 32 | 32 | } |
| 39 | 39 | for i := 0; i < 100; i++ { |
| 40 | 40 | time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) |
| 41 | 41 | bar2.Incr(1) |
| 42 | if bar2.Current() > 42 && p.RemoveBar(bar2) { | |
| 43 | break | |
| 44 | } | |
| 42 | 45 | blockSize = rand.Intn(maxBlockSize) + 1 |
| 43 | 46 | } |
| 44 | 47 | }() |
| 18 | 18 | p := mpb.New().SetWidth(64) |
| 19 | 19 | // p := mpb.New().RefreshRate(80 * time.Millisecond).SetWidth(64) |
| 20 | 20 | |
| 21 | name1 := "Bar#1:" | |
| 22 | bar1 := p.AddBar(50).AppendPercentage().PrependElapsed(3).PrependName(name1, len(name1)) | |
| 21 | name1 := "Bar#1: " | |
| 22 | bar1 := p.AddBar(50). | |
| 23 | PrependName(name1, len(name1)).PrependElapsed(3). | |
| 24 | AppendPercentage().TrimRightSpace() | |
| 23 | 25 | wg.Add(1) |
| 24 | 26 | go func() { |
| 25 | 27 | defer wg.Done() |
| 31 | 33 | } |
| 32 | 34 | }() |
| 33 | 35 | |
| 34 | bar2 := p.AddBar(100).AppendPercentage().PrependElapsed(3).PrependName("", 0-len(name1)) | |
| 36 | bar2 := p.AddBar(100). | |
| 37 | PrependName("", 0-len(name1)).PrependElapsed(3). | |
| 38 | AppendPercentage().TrimRightSpace() | |
| 35 | 39 | wg.Add(1) |
| 36 | 40 | go func() { |
| 37 | 41 | defer wg.Done() |
| 43 | 47 | } |
| 44 | 48 | }() |
| 45 | 49 | |
| 46 | bar3 := p.AddBar(80).AppendPercentage().PrependElapsed(3).PrependName("Bar#3:", 0) | |
| 50 | bar3 := p.AddBar(80). | |
| 51 | PrependName("Bar#3: ", 0).PrependElapsed(3). | |
| 52 | AppendPercentage().TrimRightSpace() | |
| 47 | 53 | wg.Add(1) |
| 48 | 54 | go func() { |
| 49 | 55 | defer wg.Done() |
| 18 | 18 | p := mpb.New().SetWidth(64) |
| 19 | 19 | // p := mpb.New().RefreshRate(100 * time.Millisecond).SetWidth(64) |
| 20 | 20 | |
| 21 | name1 := "Bar#1:" | |
| 22 | bar1 := p.AddBar(50).AppendETA().PrependPercentage(3).PrependName(name1, len(name1)) | |
| 21 | name1 := "Bar#1: " | |
| 22 | bar1 := p.AddBar(50). | |
| 23 | PrependName(name1, len(name1)).PrependETA(4). | |
| 24 | AppendPercentage().TrimRightSpace() | |
| 23 | 25 | wg.Add(1) |
| 24 | 26 | go func() { |
| 25 | 27 | defer wg.Done() |
| 31 | 33 | } |
| 32 | 34 | }() |
| 33 | 35 | |
| 34 | bar2 := p.AddBar(100).AppendETA().PrependPercentage(3).PrependName("", 0-len(name1)) | |
| 36 | bar2 := p.AddBar(100). | |
| 37 | PrependName("", 0-len(name1)).PrependETA(4). | |
| 38 | AppendPercentage().TrimRightSpace() | |
| 35 | 39 | wg.Add(1) |
| 36 | 40 | go func() { |
| 37 | 41 | defer wg.Done() |
| 39 | 43 | for i := 0; i < 100; i++ { |
| 40 | 44 | time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) |
| 41 | 45 | bar2.Incr(1) |
| 46 | if bar2.Current() > 42 && p.RemoveBar(bar2) { | |
| 47 | break | |
| 48 | } | |
| 42 | 49 | blockSize = rand.Intn(maxBlockSize) + 1 |
| 43 | 50 | } |
| 44 | 51 | }() |
| 45 | 52 | |
| 46 | bar3 := p.AddBar(80).AppendETA().PrependPercentage(3).PrependName("Bar#3:", 0) | |
| 53 | bar3 := p.AddBar(80). | |
| 54 | PrependName("Bar#3: ", 0).PrependETA(4). | |
| 55 | AppendPercentage().TrimRightSpace() | |
| 47 | 56 | wg.Add(1) |
| 48 | 57 | go func() { |
| 49 | 58 | defer wg.Done() |
| 55 | 64 | } |
| 56 | 65 | }() |
| 57 | 66 | |
| 58 | time.Sleep(3 * time.Second) | |
| 59 | // After removing the bar, it is good practice to ask underlying goroutine | |
| 60 | // (2nd one in our example) to stop, so its wg.Done() will execute in time | |
| 61 | p.RemoveBar(bar2) | |
| 62 | ||
| 63 | 67 | wg.Wait() |
| 64 | 68 | p.Stop() |
| 65 | // p.AddBar(1) // panic: you cannot reuse p, create new one! | |
| 69 | // p.AddBar(2) // panic: you cannot reuse p, create new one! | |
| 66 | 70 | fmt.Println("stop") |
| 67 | 71 | } |
| 184 | 184 | respCh <- len(bars) |
| 185 | 185 | case <-t.C: |
| 186 | 186 | width, _ := cwriter.TerminalWidth() |
| 187 | // fmt.Fprintf(os.Stderr, "twidth: %d\n", width) | |
| 188 | 187 | switch p.sort { |
| 189 | 188 | case SortTop: |
| 190 | 189 | sort.Sort(sort.Reverse(SortableBarSlice(bars))) |
| 192 | 191 | sort.Sort(SortableBarSlice(bars)) |
| 193 | 192 | } |
| 194 | 193 | for _, b := range bars { |
| 195 | // fmt.Fprintln(cw, b) | |
| 196 | 194 | buf := b.Bytes(width) |
| 197 | 195 | buf = append(buf, '\n') |
| 198 | 196 | cw.Write(buf) |