|
|
@@ -13,7 +13,7 @@ import (
|
|
|
)
|
|
|
|
|
|
// Current version
|
|
|
-const Version = "1.0.2"
|
|
|
+const Version = "1.0.25"
|
|
|
|
|
|
const (
|
|
|
// Default refresh rate - 200ms
|
|
|
@@ -37,18 +37,17 @@ func New(total int) *ProgressBar {
|
|
|
// Create new progress bar object using int64 as total
|
|
|
func New64(total int64) *ProgressBar {
|
|
|
pb := &ProgressBar{
|
|
|
- Total: total,
|
|
|
- RefreshRate: DEFAULT_REFRESH_RATE,
|
|
|
- ShowPercent: true,
|
|
|
- ShowCounters: true,
|
|
|
- ShowBar: true,
|
|
|
- ShowTimeLeft: true,
|
|
|
- ShowFinalTime: true,
|
|
|
- Units: U_NO,
|
|
|
- ManualUpdate: false,
|
|
|
- finish: make(chan struct{}),
|
|
|
- currentValue: -1,
|
|
|
- mu: new(sync.Mutex),
|
|
|
+ Total: total,
|
|
|
+ RefreshRate: DEFAULT_REFRESH_RATE,
|
|
|
+ ShowPercent: true,
|
|
|
+ ShowCounters: true,
|
|
|
+ ShowBar: true,
|
|
|
+ ShowTimeLeft: true,
|
|
|
+ ShowElapsedTime: false,
|
|
|
+ ShowFinalTime: true,
|
|
|
+ Units: U_NO,
|
|
|
+ ManualUpdate: false,
|
|
|
+ finish: make(chan struct{}),
|
|
|
}
|
|
|
return pb.Format(FORMAT)
|
|
|
}
|
|
|
@@ -67,13 +66,14 @@ func StartNew(total int) *ProgressBar {
|
|
|
type Callback func(out string)
|
|
|
|
|
|
type ProgressBar struct {
|
|
|
- current int64 // current must be first member of struct (https://code.google.com/p/go/issues/detail?id=5278)
|
|
|
+ current int64 // current must be first member of struct (https://code.google.com/p/go/issues/detail?id=5278)
|
|
|
+ previous int64
|
|
|
|
|
|
Total int64
|
|
|
RefreshRate time.Duration
|
|
|
ShowPercent, ShowCounters bool
|
|
|
ShowSpeed, ShowTimeLeft, ShowBar bool
|
|
|
- ShowFinalTime bool
|
|
|
+ ShowFinalTime, ShowElapsedTime bool
|
|
|
Output io.Writer
|
|
|
Callback Callback
|
|
|
NotPrint bool
|
|
|
@@ -81,6 +81,7 @@ type ProgressBar struct {
|
|
|
Width int
|
|
|
ForceWidth bool
|
|
|
ManualUpdate bool
|
|
|
+ AutoStat bool
|
|
|
|
|
|
// Default width for the time box.
|
|
|
UnitsWidth int
|
|
|
@@ -90,13 +91,14 @@ type ProgressBar struct {
|
|
|
finish chan struct{}
|
|
|
isFinish bool
|
|
|
|
|
|
- startTime time.Time
|
|
|
- startValue int64
|
|
|
- currentValue int64
|
|
|
+ startTime time.Time
|
|
|
+ startValue int64
|
|
|
+
|
|
|
+ changeTime time.Time
|
|
|
|
|
|
prefix, postfix string
|
|
|
|
|
|
- mu *sync.Mutex
|
|
|
+ mu sync.Mutex
|
|
|
lastPrint string
|
|
|
|
|
|
BarStart string
|
|
|
@@ -111,10 +113,11 @@ type ProgressBar struct {
|
|
|
// Start print
|
|
|
func (pb *ProgressBar) Start() *ProgressBar {
|
|
|
pb.startTime = time.Now()
|
|
|
- pb.startValue = pb.current
|
|
|
- if pb.Total == 0 {
|
|
|
+ pb.startValue = atomic.LoadInt64(&pb.current)
|
|
|
+ if atomic.LoadInt64(&pb.Total) == 0 {
|
|
|
pb.ShowTimeLeft = false
|
|
|
pb.ShowPercent = false
|
|
|
+ pb.AutoStat = false
|
|
|
}
|
|
|
if !pb.ManualUpdate {
|
|
|
pb.Update() // Initial printing of the bar before running the bar refresher.
|
|
|
@@ -128,6 +131,12 @@ func (pb *ProgressBar) Increment() int {
|
|
|
return pb.Add(1)
|
|
|
}
|
|
|
|
|
|
+// Get current value
|
|
|
+func (pb *ProgressBar) Get() int64 {
|
|
|
+ c := atomic.LoadInt64(&pb.current)
|
|
|
+ return c
|
|
|
+}
|
|
|
+
|
|
|
// Set current value
|
|
|
func (pb *ProgressBar) Set(current int) *ProgressBar {
|
|
|
return pb.Set64(int64(current))
|
|
|
@@ -165,7 +174,7 @@ func (pb *ProgressBar) Postfix(postfix string) *ProgressBar {
|
|
|
// Example: bar.Format("[\x00=\x00>\x00-\x00]") // \x00 is the delimiter
|
|
|
func (pb *ProgressBar) Format(format string) *ProgressBar {
|
|
|
var formatEntries []string
|
|
|
- if len(format) == 5 {
|
|
|
+ if utf8.RuneCountInString(format) == 5 {
|
|
|
formatEntries = strings.Split(format, "")
|
|
|
} else {
|
|
|
formatEntries = strings.Split(format, "\x00")
|
|
|
@@ -213,18 +222,34 @@ func (pb *ProgressBar) Finish() {
|
|
|
//Protect multiple calls
|
|
|
pb.finishOnce.Do(func() {
|
|
|
close(pb.finish)
|
|
|
- pb.write(atomic.LoadInt64(&pb.current))
|
|
|
- if !pb.NotPrint {
|
|
|
+ pb.write(atomic.LoadInt64(&pb.Total), atomic.LoadInt64(&pb.current))
|
|
|
+ pb.mu.Lock()
|
|
|
+ defer pb.mu.Unlock()
|
|
|
+ switch {
|
|
|
+ case pb.Output != nil:
|
|
|
+ fmt.Fprintln(pb.Output)
|
|
|
+ case !pb.NotPrint:
|
|
|
fmt.Println()
|
|
|
}
|
|
|
pb.isFinish = true
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+// IsFinished return boolean
|
|
|
+func (pb *ProgressBar) IsFinished() bool {
|
|
|
+ pb.mu.Lock()
|
|
|
+ defer pb.mu.Unlock()
|
|
|
+ return pb.isFinish
|
|
|
+}
|
|
|
+
|
|
|
// End print and write string 'str'
|
|
|
func (pb *ProgressBar) FinishPrint(str string) {
|
|
|
pb.Finish()
|
|
|
- fmt.Println(str)
|
|
|
+ if pb.Output != nil {
|
|
|
+ fmt.Fprintln(pb.Output, str)
|
|
|
+ } else {
|
|
|
+ fmt.Println(str)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// implement io.Writer
|
|
|
@@ -242,20 +267,21 @@ func (pb *ProgressBar) Read(p []byte) (n int, err error) {
|
|
|
}
|
|
|
|
|
|
// Create new proxy reader over bar
|
|
|
+// Takes io.Reader or io.ReadCloser
|
|
|
func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader {
|
|
|
return &Reader{r, pb}
|
|
|
}
|
|
|
|
|
|
-func (pb *ProgressBar) write(current int64) {
|
|
|
+func (pb *ProgressBar) write(total, current int64) {
|
|
|
width := pb.GetWidth()
|
|
|
|
|
|
- var percentBox, countersBox, timeLeftBox, speedBox, barBox, end, out string
|
|
|
+ var percentBox, countersBox, timeLeftBox, timeSpentBox, speedBox, barBox, end, out string
|
|
|
|
|
|
// percents
|
|
|
if pb.ShowPercent {
|
|
|
var percent float64
|
|
|
- if pb.Total > 0 {
|
|
|
- percent = float64(current) / (float64(pb.Total) / float64(100))
|
|
|
+ if total > 0 {
|
|
|
+ percent = float64(current) / (float64(total) / float64(100))
|
|
|
} else {
|
|
|
percent = float64(current) / float64(100)
|
|
|
}
|
|
|
@@ -265,41 +291,49 @@ func (pb *ProgressBar) write(current int64) {
|
|
|
// counters
|
|
|
if pb.ShowCounters {
|
|
|
current := Format(current).To(pb.Units).Width(pb.UnitsWidth)
|
|
|
- if pb.Total > 0 {
|
|
|
- total := Format(pb.Total).To(pb.Units).Width(pb.UnitsWidth)
|
|
|
- countersBox = fmt.Sprintf(" %s / %s ", current, total)
|
|
|
+ if total > 0 {
|
|
|
+ totalS := Format(total).To(pb.Units).Width(pb.UnitsWidth)
|
|
|
+ countersBox = fmt.Sprintf(" %s / %s ", current, totalS)
|
|
|
} else {
|
|
|
countersBox = fmt.Sprintf(" %s / ? ", current)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// time left
|
|
|
- fromStart := time.Now().Sub(pb.startTime)
|
|
|
+ pb.mu.Lock()
|
|
|
currentFromStart := current - pb.startValue
|
|
|
+ fromStart := time.Now().Sub(pb.startTime)
|
|
|
+ lastChangeTime := pb.changeTime
|
|
|
+ fromChange := lastChangeTime.Sub(pb.startTime)
|
|
|
+ pb.mu.Unlock()
|
|
|
+
|
|
|
+ if pb.ShowElapsedTime {
|
|
|
+ timeSpentBox = fmt.Sprintf(" %s ", (fromStart/time.Second)*time.Second)
|
|
|
+ }
|
|
|
+
|
|
|
select {
|
|
|
case <-pb.finish:
|
|
|
if pb.ShowFinalTime {
|
|
|
var left time.Duration
|
|
|
- if pb.Total > 0 {
|
|
|
- left = (fromStart / time.Second) * time.Second
|
|
|
- } else {
|
|
|
- left = (time.Duration(currentFromStart) / time.Second) * time.Second
|
|
|
- }
|
|
|
- timeLeftBox = left.String()
|
|
|
+ left = (fromStart / time.Second) * time.Second
|
|
|
+ timeLeftBox = fmt.Sprintf(" %s", left.String())
|
|
|
}
|
|
|
default:
|
|
|
if pb.ShowTimeLeft && currentFromStart > 0 {
|
|
|
- perEntry := fromStart / time.Duration(currentFromStart)
|
|
|
+ perEntry := fromChange / time.Duration(currentFromStart)
|
|
|
var left time.Duration
|
|
|
- if pb.Total > 0 {
|
|
|
- left = time.Duration(pb.Total-currentFromStart) * perEntry
|
|
|
+ if total > 0 {
|
|
|
+ left = time.Duration(total-currentFromStart) * perEntry
|
|
|
+ left -= time.Since(lastChangeTime)
|
|
|
left = (left / time.Second) * time.Second
|
|
|
} else {
|
|
|
left = time.Duration(currentFromStart) * perEntry
|
|
|
left = (left / time.Second) * time.Second
|
|
|
}
|
|
|
- timeLeft := Format(int64(left)).To(U_DURATION).String()
|
|
|
- timeLeftBox = fmt.Sprintf(" %s", timeLeft)
|
|
|
+ if left > 0 {
|
|
|
+ timeLeft := Format(int64(left)).To(U_DURATION).String()
|
|
|
+ timeLeftBox = fmt.Sprintf(" %s", timeLeft)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -314,30 +348,38 @@ func (pb *ProgressBar) write(current int64) {
|
|
|
speedBox = " " + Format(int64(speed)).To(pb.Units).Width(pb.UnitsWidth).PerSec().String()
|
|
|
}
|
|
|
|
|
|
- barWidth := escapeAwareRuneCountInString(countersBox + pb.BarStart + pb.BarEnd + percentBox + timeLeftBox + speedBox + pb.prefix + pb.postfix)
|
|
|
+ barWidth := escapeAwareRuneCountInString(countersBox + pb.BarStart + pb.BarEnd + percentBox + timeSpentBox + timeLeftBox + speedBox + pb.prefix + pb.postfix)
|
|
|
// bar
|
|
|
if pb.ShowBar {
|
|
|
size := width - barWidth
|
|
|
if size > 0 {
|
|
|
- if pb.Total > 0 {
|
|
|
- curCount := int(math.Ceil((float64(current) / float64(pb.Total)) * float64(size)))
|
|
|
- emptCount := size - curCount
|
|
|
+ if total > 0 {
|
|
|
+ curSize := int(math.Ceil((float64(current) / float64(total)) * float64(size)))
|
|
|
+ emptySize := size - curSize
|
|
|
barBox = pb.BarStart
|
|
|
- if emptCount < 0 {
|
|
|
- emptCount = 0
|
|
|
+ if emptySize < 0 {
|
|
|
+ emptySize = 0
|
|
|
}
|
|
|
- if curCount > size {
|
|
|
- curCount = size
|
|
|
+ if curSize > size {
|
|
|
+ curSize = size
|
|
|
}
|
|
|
- if emptCount <= 0 {
|
|
|
- barBox += strings.Repeat(pb.Current, curCount)
|
|
|
- } else if curCount > 0 {
|
|
|
- barBox += strings.Repeat(pb.Current, curCount-1) + pb.CurrentN
|
|
|
+
|
|
|
+ cursorLen := escapeAwareRuneCountInString(pb.Current)
|
|
|
+ if emptySize <= 0 {
|
|
|
+ barBox += strings.Repeat(pb.Current, curSize/cursorLen)
|
|
|
+ } else if curSize > 0 {
|
|
|
+ cursorEndLen := escapeAwareRuneCountInString(pb.CurrentN)
|
|
|
+ cursorRepetitions := (curSize - cursorEndLen) / cursorLen
|
|
|
+ barBox += strings.Repeat(pb.Current, cursorRepetitions)
|
|
|
+ barBox += pb.CurrentN
|
|
|
}
|
|
|
- barBox += strings.Repeat(pb.Empty, emptCount) + pb.BarEnd
|
|
|
+
|
|
|
+ emptyLen := escapeAwareRuneCountInString(pb.Empty)
|
|
|
+ barBox += strings.Repeat(pb.Empty, emptySize/emptyLen)
|
|
|
+ barBox += pb.BarEnd
|
|
|
} else {
|
|
|
- barBox = pb.BarStart
|
|
|
pos := size - int(current)%int(size)
|
|
|
+ barBox = pb.BarStart
|
|
|
if pos-1 > 0 {
|
|
|
barBox += strings.Repeat(pb.Empty, pos-1)
|
|
|
}
|
|
|
@@ -351,17 +393,19 @@ func (pb *ProgressBar) write(current int64) {
|
|
|
}
|
|
|
|
|
|
// check len
|
|
|
- out = pb.prefix + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix
|
|
|
- if escapeAwareRuneCountInString(out) < width {
|
|
|
- end = strings.Repeat(" ", width-utf8.RuneCountInString(out))
|
|
|
+ out = pb.prefix + timeSpentBox + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix
|
|
|
+ if cl := escapeAwareRuneCountInString(out); cl < width {
|
|
|
+ end = strings.Repeat(" ", width-cl)
|
|
|
}
|
|
|
|
|
|
// and print!
|
|
|
pb.mu.Lock()
|
|
|
+ defer pb.mu.Unlock()
|
|
|
pb.lastPrint = out + end
|
|
|
- pb.mu.Unlock()
|
|
|
+ isFinish := pb.isFinish
|
|
|
+
|
|
|
switch {
|
|
|
- case pb.isFinish:
|
|
|
+ case isFinish:
|
|
|
return
|
|
|
case pb.Output != nil:
|
|
|
fmt.Fprint(pb.Output, "\r"+out+end)
|
|
|
@@ -394,16 +438,55 @@ func (pb *ProgressBar) GetWidth() int {
|
|
|
// Write the current state of the progressbar
|
|
|
func (pb *ProgressBar) Update() {
|
|
|
c := atomic.LoadInt64(&pb.current)
|
|
|
- if pb.AlwaysUpdate || c != pb.currentValue {
|
|
|
- pb.write(c)
|
|
|
- pb.currentValue = c
|
|
|
+ p := atomic.LoadInt64(&pb.previous)
|
|
|
+ t := atomic.LoadInt64(&pb.Total)
|
|
|
+ if p != c {
|
|
|
+ pb.mu.Lock()
|
|
|
+ pb.changeTime = time.Now()
|
|
|
+ pb.mu.Unlock()
|
|
|
+ atomic.StoreInt64(&pb.previous, c)
|
|
|
+ }
|
|
|
+ pb.write(t, c)
|
|
|
+ if pb.AutoStat {
|
|
|
+ if c == 0 {
|
|
|
+ pb.startTime = time.Now()
|
|
|
+ pb.startValue = 0
|
|
|
+ } else if c >= t && pb.isFinish != true {
|
|
|
+ pb.Finish()
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// String return the last bar print
|
|
|
func (pb *ProgressBar) String() string {
|
|
|
+ pb.mu.Lock()
|
|
|
+ defer pb.mu.Unlock()
|
|
|
return pb.lastPrint
|
|
|
}
|
|
|
|
|
|
+// SetTotal atomically sets new total count
|
|
|
+func (pb *ProgressBar) SetTotal(total int) *ProgressBar {
|
|
|
+ return pb.SetTotal64(int64(total))
|
|
|
+}
|
|
|
+
|
|
|
+// SetTotal64 atomically sets new total count
|
|
|
+func (pb *ProgressBar) SetTotal64(total int64) *ProgressBar {
|
|
|
+ atomic.StoreInt64(&pb.Total, total)
|
|
|
+ return pb
|
|
|
+}
|
|
|
+
|
|
|
+// Reset bar and set new total count
|
|
|
+// Does effect only on finished bar
|
|
|
+func (pb *ProgressBar) Reset(total int) *ProgressBar {
|
|
|
+ pb.mu.Lock()
|
|
|
+ defer pb.mu.Unlock()
|
|
|
+ if pb.isFinish {
|
|
|
+ pb.SetTotal(total).Set(0)
|
|
|
+ atomic.StoreInt64(&pb.previous, 0)
|
|
|
+ }
|
|
|
+ return pb
|
|
|
+}
|
|
|
+
|
|
|
// Internal loop for refreshing the progressbar
|
|
|
func (pb *ProgressBar) refresher() {
|
|
|
for {
|
|
|
@@ -415,10 +498,3 @@ func (pb *ProgressBar) refresher() {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-type window struct {
|
|
|
- Row uint16
|
|
|
- Col uint16
|
|
|
- Xpixel uint16
|
|
|
- Ypixel uint16
|
|
|
-}
|