Codebase list golang-github-vbauerster-mpb / 50d3194
auto width support initial implementation Vladimir Bauer 9 years ago
5 changed file(s) with 166 addition(s) and 40 deletion(s). Raw diff Collapse all Expand all
33 "io"
44 "sync"
55 "time"
6 "unicode/utf8"
67 )
78
89 // Bar represents a progress Bar
2021 lastStatus int
2122
2223 incrCh chan int
23 redrawReqCh chan chan []byte
24 redrawReqCh chan *redrawRequest
2425 currentReqCh chan chan int
2526 statusReqCh chan chan int
2627 decoratorCh chan *decorator
3233 // Statistics represents statistics of the progress bar
3334 // instance of this, sent to DecoratorFunc, as param
3435 type Statistics struct {
35 Total, Current int
36 Total, Current, TermWidth int
3637 TimeElapsed, TimePerItemEstimate time.Duration
3738 }
3839
5152 width: width,
5253
5354 incrCh: make(chan int),
54 redrawReqCh: make(chan chan []byte),
55 redrawReqCh: make(chan *redrawRequest),
5556 currentReqCh: make(chan chan int),
5657 statusReqCh: make(chan chan int),
5758 decoratorCh: make(chan *decorator),
163164 }
164165
165166 // String returns the string representation of the bar
166 func (b *Bar) String() string {
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 type redrawRequest struct {
177 width int
178 respCh chan []byte
179 }
180
181 func (b *Bar) Bytes(width int) []byte {
167182 if b.isDone() {
168 return string(b.lastFrame)
183 return b.lastFrame
169184 }
170185 respCh := make(chan []byte)
171 b.redrawReqCh <- respCh
172 return string(<-respCh)
186 b.redrawReqCh <- &redrawRequest{width, respCh}
187 return <-respCh
173188 }
174189
175190 func (b *Bar) server(wg *sync.WaitGroup, total int) {
176191 timeStarted := time.Now()
177192 blockStartTime := timeStarted
178 buf := make([]byte, b.width, b.width+24)
193 // buf := make([]byte, 0, b.width+32)
179194 var tpie time.Duration
180195 var timeElapsed time.Duration
181196 var appendFuncs []DecoratorFunc
182197 var prependFuncs []DecoratorFunc
183198 var completed bool
184199 var current int
200 var termWidth int
185201 for {
186202 select {
187203 case i := <-b.incrCh:
207223 }
208224 case respCh := <-b.currentReqCh:
209225 respCh <- current
210 case respCh := <-b.redrawReqCh:
211 stat := &Statistics{total, current, timeElapsed, tpie}
212 respCh <- b.draw(stat, buf, appendFuncs, prependFuncs)
226 case r := <-b.redrawReqCh:
227 termWidth = r.width
228 stat := &Statistics{total, current, termWidth, timeElapsed, tpie}
229 r.respCh <- b.draw(stat, appendFuncs, prependFuncs)
213230 case respCh := <-b.statusReqCh:
214231 respCh <- percentage(total, current, 100)
215232 case <-b.flushedCh:
216233 if completed && !b.isDone() {
217 stat := &Statistics{total, current, timeElapsed, tpie}
218 b.lastFrame = b.draw(stat, buf, appendFuncs, prependFuncs)
234 stat := &Statistics{total, current, termWidth, timeElapsed, tpie}
235 b.lastFrame = b.draw(stat, appendFuncs, prependFuncs)
219236 b.lastStatus = percentage(total, current, 100)
220237 close(b.done)
221238 wg.Done()
231248 }
232249 }
233250
234 func (b *Bar) draw(stat *Statistics, buf []byte, appendFuncs, prependFuncs []DecoratorFunc) []byte {
235 completedWidth := percentage(stat.Total, stat.Current, b.width)
236
237 for i := 0; i < completedWidth; i++ {
251 func (b *Bar) draw(stat *Statistics, appendFuncs, prependFuncs []DecoratorFunc) []byte {
252
253 buf := make([]byte, 0, stat.TermWidth)
254
255 barBlock := b.fillBar(stat.Total, stat.Current, b.width)
256
257 // render append functions to the right of the bar
258 var appendBlock []byte
259 for _, f := range appendFuncs {
260 appendBlock = append(appendBlock, []byte(f(stat))...)
261 }
262
263 // render prepend functions to the left of the bar
264 var prependBlock []byte
265 for _, f := range prependFuncs {
266 prependBlock = append(prependBlock, []byte(f(stat))...)
267 }
268
269 prependCount := utf8.RuneCount(prependBlock)
270 barCount := utf8.RuneCount(barBlock)
271 appendCount := utf8.RuneCount(appendBlock)
272 totalCount := prependCount + barCount + appendCount
273
274 if totalCount >= stat.TermWidth {
275 newWidth := stat.TermWidth - prependCount - appendCount
276 barBlock = b.fillBar(stat.Total, stat.Current, newWidth-1)
277 }
278
279 for _, block := range [...][]byte{prependBlock, barBlock, appendBlock} {
280 buf = append(buf, block...)
281 }
282
283 return buf
284 }
285
286 func (b *Bar) fillBar(total, current, width int) []byte {
287 if width < 2 {
288 return []byte{b.leftEnd, b.rightEnd}
289 }
290 buf := make([]byte, width)
291 completedWidth := percentage(total, current, width)
292
293 for i := 1; i < completedWidth; i++ {
238294 buf[i] = b.fill
239295 }
240 for i := completedWidth; i < b.width; i++ {
296 for i := completedWidth; i < width-1; i++ {
241297 buf[i] = b.empty
242298 }
243299 // set tip bit
244 if completedWidth > 0 && completedWidth < b.width {
300 if completedWidth > 0 && completedWidth < width {
245301 buf[completedWidth-1] = b.tip
246302 }
247
248303 // set left and right ends bits
249 buf[0], buf[len(buf)-1] = b.leftEnd, b.rightEnd
250
251 // render append functions to the right of the bar
252 for _, f := range appendFuncs {
253 buf = append(buf, ' ')
254 buf = append(buf, []byte(f(stat))...)
255 }
256
257 // render prepend functions to the left of the bar
258 for _, f := range prependFuncs {
259 args := []byte(f(stat))
260 args = append(args, ' ')
261 buf = append(args, buf...)
262 }
304 buf[0], buf[width-1] = b.leftEnd, b.rightEnd
263305 return buf
264306 }
265307
0 package mpb
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 func TestFillBar(t *testing.T) {
8 b := newTestBar(80).SetEmpty('-').SetFill('=').SetTip('>').SetLeftEnd('[').SetRightEnd(']')
9 tests := []struct {
10 width int
11 want []byte
12 }{
13 {
14 width: 1,
15 want: []byte{'[', ']'},
16 },
17 {
18 width: 2,
19 want: []byte{'[', ']'},
20 },
21 {
22 width: 3,
23 want: []byte{'[', '>', ']'},
24 },
25 {
26 width: 4,
27 want: []byte{'[', '=', '>', ']'},
28 },
29 }
30
31 for _, test := range tests {
32 got := b.fillBar(80, 60, test.width)
33 if !reflect.DeepEqual(test.want, got) {
34 t.Errorf("Want: %q, Got: %q\n", test.want, got)
35 }
36 }
37 }
38
39 func newTestBar(width int) *Bar {
40 b := &Bar{
41 width: width,
42 }
43 return b
44 }
33
44 import (
55 "fmt"
6 "os"
7 "syscall"
8 "unsafe"
69 )
10
11 var tty *os.File
12
13 func init() {
14 var err error
15 tty, err = os.Open("/dev/tty")
16 if err != nil {
17 tty = os.Stdin
18 }
19 }
720
821 func (w *Writer) clearLines() {
922 for i := 0; i < w.lineCount; i++ {
1124 fmt.Fprintf(w.out, "%c[2K\r", ESC) // clear the line
1225 }
1326 }
27
28 // TerminalWidth returns width of the terminal.
29 func TerminalWidth() (int, error) {
30 w := new(window)
31 tio := syscall.TIOCGWINSZ
32 res, _, err := syscall.Syscall(syscall.SYS_IOCTL,
33 tty.Fd(),
34 uintptr(tio),
35 uintptr(unsafe.Pointer(w)),
36 )
37 if int(res) == -1 {
38 return 0, err
39 }
40 return int(w.Col), nil
41 }
42
43 type window struct {
44 Row uint16
45 Col uint16
46 Xpixel uint16
47 Ypixel uint16
48 }
1818 p := mpb.New().SetWidth(64)
1919 // p := mpb.New().RefreshRate(80 * time.Millisecond).SetWidth(64)
2020
21 name1 := "Bar#1:"
22 bar1 := p.AddBar(50).AppendPercentage().PrependETA(4).PrependName(name1, len(name1))
21 name1 := "Bar#1: "
22 bar1 := p.AddBar(50).AppendPercentage().PrependName(name1, len(name1)).PrependETA(4)
2323 wg.Add(1)
2424 go func() {
2525 defer wg.Done()
3131 }
3232 }()
3333
34 bar2 := p.AddBar(100).AppendPercentage().PrependETA(4).PrependName("", 0-len(name1))
34 bar2 := p.AddBar(100).AppendPercentage().PrependName("", 0-len(name1)).PrependETA(4)
3535 wg.Add(1)
3636 go func() {
3737 defer wg.Done()
4343 }
4444 }()
4545
46 bar3 := p.AddBar(80).AppendPercentage().PrependETA(4).PrependName("Bar#3:", 0)
46 bar3 := p.AddBar(80).AppendPercentage().PrependName("Bar#3: ", 0).PrependETA(4)
4747 wg.Add(1)
4848 go func() {
4949 defer wg.Done()
11
22 import (
33 "errors"
4 "fmt"
54 "io"
65 "os"
76 "sort"
184183 case respCh := <-p.countReqCh:
185184 respCh <- len(bars)
186185 case <-t.C:
186 width, _ := cwriter.TerminalWidth()
187 // fmt.Fprintf(os.Stderr, "twidth: %d\n", width)
187188 switch p.sort {
188189 case SortTop:
189190 sort.Sort(sort.Reverse(SortableBarSlice(bars)))
191192 sort.Sort(SortableBarSlice(bars))
192193 }
193194 for _, b := range bars {
194 fmt.Fprintln(cw, b)
195 // fmt.Fprintln(cw, b)
196 buf := b.Bytes(width)
197 buf = append(buf, '\n')
198 cw.Write(buf)
195199 }
196200 cw.Flush()
197201 for _, b := range bars {