Giter Club home page Giter Club logo

uiprogress's Introduction

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.

uiprogress's People

Contributors

acasajus avatar bitdeli-chef avatar boz avatar collinux avatar etowah avatar gosuri avatar heinoldewage avatar henvic avatar kothar avatar mgurov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uiprogress's Issues

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()

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

example/simple: progress bar scrolls terminal

hi,

looking at #3 and event after specifying the bar.Width to, say, 60:

$> git diff
diff --git a/example/simple/simple.go b/example/simple/simple.go
index d8d912b..2af47af 100644
--- a/example/simple/simple.go
+++ b/example/simple/simple.go
@@ -9,6 +9,7 @@ import (
 func main() {
        uiprogress.Start()            // start rendering
        bar := uiprogress.AddBar(100) // Add a new bar
+       bar.Width = 60

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

I still get:

$> go run ./simple.go
    0 [----------------------------------------------------------]   1%
    0 [----------------------------------------------------------]   2%
    0 [----------------------------------------------------------]   2%
[...]
   1s [=========================================================>]  99%
   1s [=========================================================>]  99%
   1s [==========================================================] 100%
   1s [==========================================================] 100%

my terminal is rxvt:

$> echo $TERM
rxvt-unicode-256color

(but even with TERM=vt100, I get the above behaviour)

is there something I am doing wrong?

Allow for utf8 chatacters

It may be desirable to have runes that are wider than a byte (such as โ–ถ for the uiprogress.Head). Additionally it would be nice to support not only runes, but also short strings so that ansi control charaters can be used (for color).

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")

First progress bar is too long

Doing something pretty simple here and I'm not sure why the display is wrong.
[--------------------------------------------------------------------] 38/3805-------] 0/3805

	uiprogress.Start()         
	bar := uiprogress.AddBar(filesCount) 


	bar.AppendFunc(func(b *uiprogress.Bar) string {
		return fmt.Sprintf("%d/%d", b.Current(), b.Total)
	})

	for _, file := range files {
                 //process
		bar.Incr()
	}

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%

always update bar on stop

There's a race between the refresh interval and the end time of the app, if the app finishes and exits before the refresh interval allowed 100% to be printed you get like 75% printed.

Seems unavoidable on the design of the updated routine which is fine, can we perhaps have an implicit print called in Stop? This would ensure on stop the final status is always printed

Remove bar from uiprogress

Thanks for this project, it's very useful!

This is rather question (and maybe feature request):

  • How can I remove progress bar after calling uiprogress.AddBar()?
  • Also, how can I stop the rendering?

Progress keeps printing after .Stop()

Simple demonstration:

func ExampleStoppingPrintout() {
    progress := uiprogress.New()
    progress.RefreshInterval = time.Millisecond * 10
    progress.Start()
    bar := progress.AddBar(1)
    bar.Incr()
    time.Sleep(time.Millisecond * 15)
        //workaround
    //progress.Bars = nil
    progress.Stop()
    time.Sleep(1 * time.Second)
    // Output: [====================================================================]
}

Expected: bar once printed, as it is stopped after one interval.
Actual: The bar is printed every 10 milliseconds.

Start and Stop twice make program panic

Hello,
I may be missing a point, but it seems that starting and stoping a uiprogress twice make the program panic.

Please find bellow a short test program:

package main

import (
    "fmt"
    "time"

    "github.com/gosuri/uiprogress"
)

func createbar() {
    uiprogress.Start()
    defer uiprogress.Stop()
    // create a new bar and prepend the task progress to the bar
    count := 100
    bar := uiprogress.AddBar(count).AppendCompleted().PrependElapsed()
    bar.PrependFunc(func(b *uiprogress.Bar) string {
        return fmt.Sprintf("Task (%d/%d)", b.Current(), count)
    })

    for i := 0; i < count; i++ {
            bar.Incr()
            time.Sleep(time.Millisecond * time.Duration(50))
    }
    time.Sleep(time.Millisecond * time.Duration(500))
}

func main() {
  createbar()
  createbar()
}

The console:

$ go run main.go
Task (100/100)    5s [====================================================================] 100%
Task (1/100)    0s [--------------------------------------------------------------------]   1%
panic: close of closed channel

goroutine 20 [running]:
github.com/gosuri/uiprogress.(*Progress).Listen(0xc00007a1e0)
	/Users/jtbonhomme/Developments/golang/src/github.com/gosuri/uiprogress/progress.go:117 +0x12f
created by github.com/gosuri/uiprogress.(*Progress).Start
	/Users/jtbonhomme/Developments/golang/src/github.com/gosuri/uiprogress/progress.go:134 +0x3f
exit status 2

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?

Progress bars get multiplied

My program is creating 2 Bars like so:

        uiprogress.Start()

	bar := uiprogress.AddBar(int(file.Size())).PrependElapsed().
		AppendCompleted()
	bar.PrependFunc(func(b *uiprogress.Bar) string {
		return paddedName
	})
	bar.Fill = '#'
	bar.Head = '#'
	bar.Empty = ' '

        // In a calback, keep calling this until done:
       bar.Set(int(count))

       uiprogress.Stop()

I would expect to see 2 lines of output, one per bar -- instead I am seeing the following:

Bar1   --- [#############################################################       ]  90%
Bar1   --- [####################################################################] 100%
Bar2  --- [#############################################################       ]  90%
Bar1   --- [####################################################################] 100%
Bar2  --- [####################################################################] 100%

I am creating the bars in arbitrary goroutines. Is this an issue? Should all the bars be created in a single goroutine?

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.

int to int64

In go std library almost return value of something get is int64
So I think to change int to int64 is better and type safe
How about it?

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

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

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.