diff --git a/examples/main.go b/examples/main.go index fe15c23..19c5908 100644 --- a/examples/main.go +++ b/examples/main.go @@ -1,6 +1,7 @@ package main import ( + "os" "time" ansi "github.com/k0kubun/go-ansi" @@ -17,15 +18,16 @@ func main() { time.Sleep(2 * time.Millisecond) } - // // bar with options - // bar = progressbar.NewOptions(1000, - // progressbar.OptionSetWriter(os.Stderr), - // progressbar.OptionSetTheme(progressbar.Theme{Saucer: "~", SaucerPadding: "-", BarStart: "|", BarEnd: "|"}), - // ) - // for i := 0; i < 1000; i++ { - // bar.Add(1) - // time.Sleep(2 * time.Millisecond) - // } + // bar with options + bar = progressbar.NewOptions(1000, + progressbar.OptionSetWriter(os.Stderr), + progressbar.OptionSetTheme(progressbar.Theme{Saucer: "~", SaucerPadding: "-", BarStart: "|", BarEnd: "|"}), + progressbar.OptionThrottle(100*time.Millisecond), + ) + for i := 0; i < 1000; i++ { + bar.Add(1) + time.Sleep(2 * time.Millisecond) + } bar = progressbar.NewOptions(100, progressbar.OptionSetWriter(ansi.NewAnsiStdout()), diff --git a/progressbar.go b/progressbar.go index 80c302c..ff9e6fd 100644 --- a/progressbar.go +++ b/progressbar.go @@ -57,6 +57,9 @@ type config struct { maxBytes int // show the iterations per second showIterationsPerSecond bool + + // minimum time to wait in between updates + throttleDuration time.Duration } // Theme defines the elements of the bar @@ -128,6 +131,13 @@ func OptionShowIts() Option { } } +// OptionShowIts will also print the iterations/second +func OptionThrottle(duration time.Duration) Option { + return func(p *ProgressBar) { + p.config.throttleDuration = duration + } +} + var defaultTheme = Theme{Saucer: "█", SaucerPadding: " ", BarStart: "|", BarEnd: "|"} // NewOptions constructs a new instance of ProgressBar, with any options you specify @@ -135,10 +145,11 @@ func NewOptions(max int, options ...Option) *ProgressBar { b := ProgressBar{ state: getBlankState(), config: config{ - writer: os.Stdout, - theme: defaultTheme, - width: 40, - max: max, + writer: os.Stdout, + theme: defaultTheme, + width: 40, + max: max, + throttleDuration: 0 * time.Nanosecond, }, } @@ -226,8 +237,16 @@ func (p *ProgressBar) Clear() error { // rendered line width. this function is not thread-safe, // so it must be called with an acquired lock. func (p *ProgressBar) render() error { + // make sure that the rendering is not happening too quickly + if time.Since(p.state.lastShown).Nanoseconds() < p.config.throttleDuration.Nanoseconds() { + return nil + } + // first, clear the existing progress bar err := clearProgressBar(p.config, p.state) + if err != nil { + return err + } // then, re-render the current progress bar w, err := renderProgressBar(p.config, p.state) @@ -239,6 +258,8 @@ func (p *ProgressBar) render() error { p.state.maxLineWidth = w } + p.state.lastShown = time.Now() + return nil } diff --git a/progressbar_test.go b/progressbar_test.go index 422e58a..0ea575e 100644 --- a/progressbar_test.go +++ b/progressbar_test.go @@ -16,6 +16,16 @@ func ExampleProgressBar() { // 10% |█ | [1s:9s] } +func ExampleThrottle() { + bar := NewOptions(100, OptionSetWidth(10), OptionSetRenderBlankState(false), OptionThrottle(100*time.Millisecond)) + bar.Reset() + bar.Add(5) + time.Sleep(150 * time.Millisecond) + bar.Add(5) + bar.Add(10) + // Output: + // 10% |█ | [0s:1s] +} func ExampleFinish() { bar := NewOptions(100, OptionSetWidth(10), OptionSetRenderBlankState(false)) bar.Reset() @@ -102,7 +112,6 @@ func TestOptionSetTheme(t *testing.T) { OptionSetWidth(10), OptionSetWriter(&buf), ) - bar.Add(5) result := strings.TrimSpace(buf.String()) expect := "50% >#####-----< [0s:0s]"