A go library to render progress bars in terminal applications

uiprogress GoDoc Build Status

A Go library to render progress bars in terminal applications. It provides a set of flexible features with a customizable API.

example

Progress bars improve readability for terminal applications with long outputs by providing a concise feedback loop.

Features

  • Multiple Bars: uiprogress can render multiple progress bars that can be tracked concurrently
  • Dynamic Addition: Add additional progress bars any time, even after the progress tracking has started
  • Prepend and Append Functions: Append or prepend completion percent and time elapsed to the progress bars
  • Custom Decorator Functions: Add custom functions around the bar along with helper functions

Usage

To start listening for progress bars, call uiprogress.Start() and add a progress bar using uiprogress.AddBar(total int). Update the progress using bar.Incr() or bar.Set(n int). Full source code for the below example is available at example/simple/simple.go

uiprogress.Start()            // start rendering
bar := uiprogress.AddBar(100) // Add a new bar

// optionally, append and prepend completion and elapsed time
bar.AppendCompleted()
bar.PrependElapsed()

for bar.Incr() {
  time.Sleep(time.Millisecond * 20)
}

This will render the below in the terminal

example

Using Custom Decorators

You can also add a custom decorator function in addition to default bar.AppendCompleted() and bar.PrependElapsed() decorators. The below example tracks the current step for an application deploy progress. Source code for the below example is available at example/full/full.go

var steps = []string{"downloading source", "installing deps", "compiling", "packaging", "seeding database", "deploying", "staring servers"}
bar := uiprogress.AddBar(len(steps))

// prepend the current step to the bar
bar.PrependFunc(func(b *uiprogress.Bar) string {
  return "app: " + steps[b.Current()-1]
})

for bar.Incr() {
  time.Sleep(time.Millisecond * 10)
}

Rendering Multiple bars

You can add multiple bars using uiprogress.AddBar(n). The below example demonstrates updating multiple bars concurrently and adding a new bar later in the pipeline. Source for this example is available at example/multi/multi.go

waitTime := time.Millisecond * 100
uiprogress.Start()

// start the progress bars in go routines
var wg sync.WaitGroup

bar1 := uiprogress.AddBar(20).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
  defer wg.Done()
  for bar1.Incr() {
    time.Sleep(waitTime)
  }
}()

bar2 := uiprogress.AddBar(40).AppendCompleted().PrependElapsed()
wg.Add(1)
go func() {
  defer wg.Done()
  for bar2.Incr() {
    time.Sleep(waitTime)
  }
}()

time.Sleep(time.Second)
bar3 := uiprogress.AddBar(20).PrependElapsed().AppendCompleted()
wg.Add(1)
go func() {
  defer wg.Done()
  for i := 1; i <= bar3.Total; i++ {
    bar3.Set(i)
    time.Sleep(waitTime)
  }
}()

// wait for all the go routines to finish
wg.Wait()

This will produce

example

Incr counter

Bar.Incr() is an atomic counter and can be used as a general tracker, making it ideal for tracking progress of work fanned out to a lots of go routines. The source code for the below example is available at example/incr/incr.go

runtime.GOMAXPROCS(runtime.NumCPU()) // use all available cpu cores

// create a new bar and prepend the task progress to the bar and fanout into 1k go routines
count := 1000
bar := uiprogress.AddBar(count).AppendCompleted().PrependElapsed()
bar.PrependFunc(func(b *uiprogress.Bar) string {
  return fmt.Sprintf("Task (%d/%d)", b.Current(), count)
})

uiprogress.Start()
var wg sync.WaitGroup

// fanout into go routines
for i := 0; i < count; i++ {
  wg.Add(1)
  go func() {
    defer wg.Done()
    time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))
    bar.Incr()
  }()
}
time.Sleep(time.Second) // wait for a second for all the go routines to finish
wg.Wait()
uiprogress.Stop()

Installation

$ go get -v github.com/gosuri/uiprogress

Todos

  • Resize bars and decorators by auto detecting window's dimensions
  • Handle more progress bars than vertical screen allows

License

uiprogress is released under the MIT License. See LICENSE.

Owner
Greg Osuri
Founder & CEO, Akash Network / Overclock Labs
Greg Osuri
Comments
  • Using time.Ticker for running stoppable listener. Fix edge case where flush happens after the listener is stopped.

    Using time.Ticker for running stoppable listener. Fix edge case where flush happens after the listener is stopped.

    This should close #18 and #20.

    @mgurov, can you take a look at the changes I have made on top of your code (which I actually rebased), please?

    https://github.com/gosuri/uilive/pull/7 is also needed.

  • fmt.println re-prints last progress bar

    fmt.println re-prints last progress bar

    Hello,

    I'm not sure if I'm doing something wrong or it's a bug. When I execute the code below no matter what I do the progress bar will increment to 100% then when it hits the next fmt.Println it will re-print the previous progress bar at 100%.

    uiprogress.Start()
    bar := uiprogress.AddBar(length)
    bar.AppendCompleted()
    bar.PrependElapsed()
    for _, eachFilter := range filters.Items {
        bar.Incr()
        if something exists {
            do something
        }
    }
    uiprogress.Stop()
    bar = nil
    fmt.Println("print something")
    
  • Add Incr

    Add Incr

    The current Set(int) for setting progress works great for the common iterator, but doesn't really work for tracking of progress of work fanned out to a lot of go routines. How about an atomic Incr function?

  • Make the progress bar concurrent safe

    Make the progress bar concurrent safe

    The API looks good, the implementation will experience data races when used to collect progress from many go routines.

    Since bar has more than the simple counter as state, a RWMutex seems like a good fit.

  • Need a way to wait progress bar output its final state

    Need a way to wait progress bar output its final state

    package main
    
    import (
        "time"
    
        "github.com/gosuri/uiprogress"
    )
    
    func main() {
        uiprogress.Start()            // start rendering
        bar := uiprogress.AddBar(100) // Add a new bar
    
        // optionally, append and prepend completion and elapsed time
        bar.AppendCompleted()
        bar.PrependElapsed()
    
        for bar.Incr() {
            time.Sleep(time.Millisecond * 1)
        }
    }
    

    If you reduce sleep time (less than uiprogress.RefreshInterval), the progress bar will not reach 100%, see:

    $ go run example/simple/simple.go 
        0 [=================================================================>--]  97%
    

    I think there should have a mechanism to make sure the outputs are consistent. A Flush() method?

    For example:

    for bar.Incr() {
        time.Sleep(time.Millisecond * 1)
    }
    uiprogress.Flush()
    
  • Data race in example/simple.go

    Data race in example/simple.go

    The data race occurs in AddBar and Listen on the accessing of p.Bars.

    Not a big problem, but the example should be clear that the setup should be done before calling Start. Alternatively AddBar and Listen could be made thread safe.

    Running go run -race simple.go give the following output:

    C:\Go Programs\src\github.com\gosuri\uiprogress\example\simple>go run -race simple.go
    ==================
    WARNING: DATA RACE
    Read by goroutine 7:
      github.com/gosuri/uiprogress.(*Progress).Listen.func1()
          C:/Go programs/src/github.com/gosuri/uiprogress/progress.go:87 +0x89
    
    Previous write by main goroutine:
      main.main()
          C:/Go Programs/src/github.com/gosuri/uiprogress/example/simple/simple.go:11 +0x30e
    
    Goroutine 7 (running) created at:
      github.com/gosuri/uiprogress.(*Progress).Listen()
          C:/Go programs/src/github.com/gosuri/uiprogress/progress.go:92 +0xb7
    ==================
    ==================
    WARNING: DATA RACE
    Read by goroutine 7:
      github.com/gosuri/uiprogress.(*Progress).Listen.func1()
          C:/Go programs/src/github.com/gosuri/uiprogress/progress.go:87 +0xea
    
    Previous write by main goroutine:
      main.main()
          C:/Go Programs/src/github.com/gosuri/uiprogress/example/simple/simple.go:11 +0x2d8
    
    Goroutine 7 (running) created at:
      github.com/gosuri/uiprogress.(*Progress).Listen()
          C:/Go programs/src/github.com/gosuri/uiprogress/progress.go:92 +0xb7
    ==================
       2s [====================================================================] 100%
    Found 2 data race(s)
    exit status 66
    
  • Vertical scroll outputs new rendering on vertical scroll

    Vertical scroll outputs new rendering on vertical scroll

    Possibly related to #3 , if subsequent output scrolls the terminal past the vertical limit then a duplicate copy of the progress bar is rendered for each "full" vertical scroll.

    screen shot 2017-03-23 at 5 35 44 pm screen shot 2017-03-23 at 5 35 50 pm
  • Stop does not stop printing to screen.

    Stop does not stop printing to screen.

    Calling Stop exits Listen, but goroutine started at progress.go:92 does not stop.

    This keeps printing over the last line.

    A simple demonstration would be to add the following code at the end of simple.go:

        uiprogress.Stop()
        for {
            log.Println("After loop")
            time.Sleep(time.Millisecond * 10)
        }
    

    The output after the progress completes is the following rather than many 'After loop' lines:

    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    2s [====================================================================] 100%
    
  • Add `Label` to Bars

    Add `Label` to Bars

    This basically adds a string to the far left of the bar. Used like this:

    bar := uiprogress.NewBar(100, "app1")
    

    it is equivalent to:

    bar := uiprogress.NewBar(100)
    bar.PrependFunc(func(_ *uiprogress.Bar) string {
        return "app1"
    })
    
  • How to use it in network IO

    How to use it in network IO

    count := len(num)
    bar1 := uiprogress.AddBar(count).AppendCompleted().PrependElapsed()
    bar1.PrependFunc(func(b *uiprogress.Bar) string {
    	return fmt.Sprintf("Task (%d/%d)", b.Current(), count)
    })	
    

    for bar1.Incr() { //time.Sleep(time.Second * 5) for i := 0; i < count; i++ { s := rand.Float32() time.Sleep(time.Second * time.Duration(s)) tsUrl := fmt.Sprintf("%s", baseUrl+num[i]) req, _ := http.NewRequest("GET", baseUrl, nil) req.Header.Add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body)

    		if len(key) != 0 {
    			data, _ = AesCrypt.AesDecrypt(body, key, iv)
    		} else {
    			data = []byte(string(body))
    		}
    		write.Write(data)
    
    
    	}
    	write.Flush()
    }
    

    This will always show the first progress bar after the request is sent

  • Set function updates the elapsed time.

    Set function updates the elapsed time.

    When using the Set() function, then the elapsed time does not update. While it is updating when using the Incr() function. This PR fix the issue #34 .

  • Adding ppc64le architecture support on travis-ci

    Adding ppc64le architecture support on travis-ci

    Hi, I had added ppc64le(Linux on Power) architecture support on travis-ci in the PR and looks like its been successfully added. I believe it is ready for the final review and merge. The travis ci build logs can be verified from the link below. https://travis-ci.com/github/kishorkunal-raj/uiprogress/builds/191399948

    Reason behind running tests on ppc64le: This package is included in the ppc64le versions of RHEL and Ubuntu - this allows the top of tree to be tested continuously as it is for Intel, making it easier to catch any possible regressions on ppc64le before the distros begin their clones and builds. This reduces the work in integrating this package into future versions of RHEL/Ubuntu.

    Please have a look.

    Regards, Kishor Kunal Raj

  • Proposal: Please tag a new release and using Semantic Versioning

    Proposal: Please tag a new release and using Semantic Versioning

    I found that the latest version is v0.0.1 committed on 24 Feb 2017. When module users try to get github.com/gosuri/uiprogress, the version v0.0.1 will be return.

    $go get -v github.com/gosuri/uiprogress
    go: downloading github.com/gosuri/uiprogress v0.0.1
    go: github.com/gosuri/uiprogress upgrade => v0.0.1
    

    It would be great to have a new tagged release be named in the format vX.X.X format so that go mod can read it.

    So, I propose this project to tag a new release and follow Semantic Versioning in future versions. For example, v1.0.1, v2.0.0, v3.1.0-alpha, v3.1.0-beta.2etc.

  • Print logs on top and bar at bottom

    Print logs on top and bar at bottom

    In trying to reproduce the apt progress bar I adapted https://github.com/gosuri/uiprogress/blob/ec0f254bbca2b17ed68f18af8773a0527b522f01/example/simple/simple.go so that some lines are printed:

    package main
    
    import (
            "time"
            "fmt"
    
            "github.com/gosuri/uiprogress"
    )
    
    func main() {
            uiprogress.Start()            // start rendering
            bar := uiprogress.AddBar(100) // Add a new bar
    
            // optionally, append and prepend completion and elapsed time
            bar.AppendCompleted()
            bar.PrependElapsed()
    
            i := 0
            for bar.Incr() {
                    time.Sleep(time.Millisecond * 20)
                    i++
                    if i % 10 == 0 {
                            fmt.Println("Well hello there")
                    }
            }
    }
    

    I'd like for the lines to print above the progress bar, which would stay at the bottom. However I'm only getting duplicated progress and only one line printed (also not at the right place but I'm sure that's another issue):

       0s [=====>--------------------------------------------------------------]  10%
       0s [============>-------------------------------------------------------]  20%
       0s [===================>------------------------------------------------]  30%
       0s [==========================>-----------------------------------------]  40%
       0s [=================================>----------------------------------]  50%
       1s [========================================>---------------------------]  60%
       1s [===============================================>--------------------]  70%
       1s [======================================================>-------------]  80%
       1s [=============================================================>------]  90%
       1s [====================================================================] 100%
    Well hello there
    
A really basic thread-safe progress bar for Golang applications
A really basic thread-safe progress bar for Golang applications

progressbar A very simple thread-safe progress bar which should work on every OS without problems. I needed a progressbar for croc and everything I tr

Jan 2, 2023
Console Text Colors - The non-invasive cross-platform terminal color library does not need to modify the Print method

ctc - Console Text Colors The non-invasive cross-platform terminal color library does not need to modify the Print method Virtual unix-like environmen

Nov 9, 2022
uilive is a go library for updating terminal output in realtime
uilive is a go library for updating terminal output in realtime

uilive uilive is a go library for updating terminal output in realtime. It provides a buffered io.Writer that is flushed at a timed interval. uilive p

Dec 28, 2022
A go library to improve readability in terminal apps using tabular data

uitable uitable is a go library for representing data as tables for terminal applications. It provides primitives for sizing and wrapping columns to i

Dec 30, 2022
Intuitive package for prettifying terminal/console output. http://godoc.org/github.com/ttacon/chalk
Intuitive package for prettifying terminal/console output. http://godoc.org/github.com/ttacon/chalk

chalk Chalk is a go package for styling console/terminal output. Check out godoc for some example usage: http://godoc.org/github.com/ttacon/chalk The

Dec 23, 2022
An ANSI colour terminal package for Go

colourize An ANSI colour terminal package for Go. Supports all ANSI colours and emphasis. Not compatible with Windows systems. Installation go get gi

Sep 26, 2022
Simple tables in terminal with Go

Simple tables in terminal with Go This package allows to generate and display ascii tables in the terminal, f.e.: +----+------------------+-----------

Dec 29, 2022
Terminal based dashboard.
Terminal based dashboard.

Termdash is a cross-platform customizable terminal based dashboard. The feature set is inspired by the gizak/termui project, which in turn was inspire

Dec 28, 2022
Golang terminal dashboard
Golang terminal dashboard

termui termui is a cross-platform and fully-customizable terminal dashboard and widget library built on top of termbox-go. It is inspired by blessed-c

Dec 27, 2022
Yet Another CLi Spinner; providing over 70 easy to use and customizable terminal spinners for multiple OSes
Yet Another CLi Spinner; providing over 70 easy to use and customizable terminal spinners for multiple OSes

Yet Another CLi Spinner (for Go) Package yacspin provides yet another CLi spinner for Go, taking inspiration (and some utility code) from the https://

Dec 25, 2022
Terminal string styling for go done right, with full and painless Windows 10 support.
Terminal string styling for go done right, with full and painless Windows 10 support.

GChalk GChalk is a library heavily inspired by chalk, the popular Node.js terminal color library, and using go ports of supports-color and ansi-styles

Dec 28, 2022
Small library for simple and convenient formatted stylized output to the console.
Small library for simple and convenient formatted stylized output to the console.

cfmt cfmt is a small library for simple and convenient formatted stylized output to the console, providing an interface that is exactly the same as th

Jan 7, 2023
A tiny library for super simple Golang tables

Tabby A tiny library for super simple Golang tables Get Tabby go get github.com/cheynewallace/tabby Import Tabby import "github.com/cheynewallace/tabb

Nov 23, 2022
hierarchical progress bars in terminal on steroids
hierarchical progress bars in terminal on steroids

Echelon - hierarchical progress in terminals Cross-platform library to organize logs in a hierarchical structure. Here is an example how it looks for

Nov 26, 2022
Utilities to prettify console output of tables, lists, progress-bars, text, etc.
Utilities to prettify console output of tables, lists, progress-bars, text, etc.

go-pretty Utilities to prettify console output of tables, lists, progress-bars, text, etc. Table Pretty-print tables into ASCII/Unicode strings.

Dec 29, 2022
A Simple Anonym FileSharing Service which is able to be render in the Browser and in an Terminal-Client

FileSharingService A Simple Anonym FileSharing Service which is able to be render in the Browser and in an Terminal-Client How to use Take a look in t

Dec 25, 2022
Terminal based wordle clone. Uses the amazing charm.sh libraries to render and expose the game over SSH

Terminal based wordle clone. Uses the amazing charm.sh libraries to render and expose the game over SSH

Nov 17, 2022
Using Eww Widgets to create a replacement for my bumblebee-status bars in i3 Window Manager.
Using Eww Widgets to create a replacement for my bumblebee-status bars in i3 Window Manager.

Eww Config What is it Using Eww Widgets to create a replacement for my bumblebee-status bars in i3 Window Manager. Eww allows you to create widgets an

Nov 18, 2021
OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc
OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc

graph-bars-font OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc. I didn't find anything similar on the net so I decided

Jul 28, 2022