In this assignment, you will create a professional-looking “progress bar” package, that others can use to add progress bar functionality to their apps. (You may find it useful to use in your own projects!)
Create a package called progress
. It should expose a type called ProgressBar
. Likely, you will choose to use a struct as the underlying type for your progress bar.
The type should have several public methods:
func (pb *ProgressBar) Start(total int)
. This starts the progress bar. It prints an empty progress bar to the screen, which should look something like:
0 / 400 |----------------------------------------| 0.0%, calculating time remaining
The argument, total
, is how many items the user of your progress bar will be processing; in this case, it would be 400. The Start method also “remembers” the current time (using the time.Now()
function). Why? As items complete (see the Increment()
method below), your progress bar keeps track of the average amount of time necessary to complete an item. It will use this later to estimate “time remaining.”
Note: you should use fmt.Print
or fmt.Printf
, not fmt.Println
. This will allow a future fmt.Print
statement to overwrite this one, “updating” the progress bar.
func (pb *ProgressBar) Increment()
. Assuming the progress bar has been Start
ed, this completes one additional item, and redraws the progress bar, perhaps to look like this:
30 / 400 |==>-------------------------------------| 7.5%, 1m2s remaining
When printing the new progress bar, begin your line with "\r"
, a “carriage return”, which causes the cursor to return to the beginning of the line and overwrite the previously written line.
A couple notes:
In my example here, the progress bar contains 40 -
characters. We calculate 7.5% of 40, which is 3, so we know 3 of them should be filled in. (In general, you can round down.) Two of the three, we fill in with an =
symbol, and the third, with a >
, so that it looks like an arrow making forward movement. You may choose your own style if you don’t like this one (for example, maybe use the █ character [copy and paste it from here] for “filled in” parts of the progress bar, and ▂ for the rest), but the logic will be mostly the same.
Each time Increment
is called, you should re-calculate the average time per item. Use the time.Since(startTime)
function to calculate how long it has been since a certain time (the start time); this will return a value of type time.Duration
. (Duration has underlying type int64
, and represents a number of nanoseconds.) You can then divide this by the number of items processed so far to get the average duration for processing an item. (To do this, write, e.g., avgDuration := totalDuration / time.Duration(itemsSoFar)
; since itemsSoFar
and totalDuration
are different types, you need to convert itemsSoFar
into a ‘duration’ before using it in a division.) Multiply this number by time.Duration(itemsLeftToProcess)
, which will give you the estimated time remaining. To turn this Duration value into something human-readable, use the Minutes()
and Seconds()
methods of the duration type, which return ints.
If a call to Increment
finishes the progress bar, you should print a new line, ending the progress bar. Future calls to Increment should do nothing.
(pb ProgressBar) IsActive() bool
. Returns true
if the progress bar is “still active” (i.e., not finished yet), or false
otherwise.
You can add any other methods or functions you’d like, but they should not be public methods (i.e., use lowercase letters).
In a separate folder, create the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package main import "fmt" import "time" import "math/rand" // you will also need to import your package func main() { fmt.Println("Welcome. Now processing items...") var pb progress.ProgressBar // Say that we have 90 items to process, and keep processing // while the progress bar is active. pb.Start(90) for pb.IsActive() { pb.Increment() timeToProcess := time.Duration(300+rand.Intn(200)) * time.Millisecond time.Sleep(timeToProcess) } fmt.Println("Finished processing items.") } |
You do not need to upload this main package, just use it to test your program. Upload your progress bar package as YourNameProgressBar.go. You should leave a Google Classroom comment describing how your testing went, and if there are any known bugs.