Develop a DIY progress bar
Channels and Routines
Go offers so-called goroutines and channels (as discussed in a previous issue [4]) for firing off concurrent program parts and their synchronization. Listing 2 shows how to illustrate the long copying process for a large file in Go with a progress bar from the termui
package. After installing the UI package like this
$ go get github.com/gizak/termui
and building the program like this
$ go build cpgui.go
the call to cpgui foo
copies a file named foo
to foo.bak
. If it is relatively large, you can follow along by watching the terminal UI's progress bar drawn on the terminal, gradually growing to the right until it reaches the end of the display element, at which point the file has been copied and the program terminates (Figure 4).
Line 19 of Listing 2 defines a new structure using NewGauge()
from the termui
package; this represents the UI element of a progress bar. The initial value of the bar growing from left to right is set to
with the Percent
attribute; in other words, the bar is at zero percent and thus completely on the left and invisible. Lines 21 to 26 define further attributes, such as the colors of the bar's individual components, their size, or the caption for the graphic element.
Communication Channels
Listing 2 uses the channels defined in lines 29 and 30, named update
and done
, for the communication between the different program parts that write the data or refresh the bar.
The backup()
function as of line 56 receives the update
channel as a parameter from the main program and uses it to update the progress bar by sending the completed percentage into it, as soon as it has written a chunk of bytes to the file. On the receiving end of the channel, the main()
function waits for new messages in a concurrent goroutine running an infinite loop starting in line 40. The read function on the channel in line 41 blocks if no new values have been written to the channel yet by the backup()
function. When a new integer value arrives in the channel, line 41 updates the progress bar's Percent
attribute in g
to the new percentage value and then redraws the graphic element with ui.Render(g)
.
The second channel, done
, allows the backup()
function, which also receives this channel as a parameter from the main program, to initiate the end of the program. For this to happen, the main function asynchronously waits for data in the done
channel in a goroutine starting in line 33. As soon as data becomes available (because lines 60, 70, or 84 have sent a true
into the channel), line 35 triggers ui.StopLoop()
in order to close the UI event loop, which tears down the GUI in line 53 and terminates the main()
function.
In this simplified example, the ReadFile()
function reads the source file data via the ioutil
package in one fell swoop. If the package isn't installed yet, you do so by typing go get io/ioutil
. The data ends up in an array slice holding elements of the Byte
type. The len()
function in line 64 determines the length of the file in bytes and stores it in the total
variable. Line 67 creates the new file with the .bak
extension on the filesystem and returns a writer interface in out
. The interface is then passed onto the Write()
function, which sends 4096-byte chunks in line 78 until all the bytes of the original file have been successfully copied. Reading all data into memory at once is obviously not a good idea for large files; in a real-world application, you'd want to read the data in chunks and write it as it becomes available, updating the progress bar accordingly.
In the write loop, the next 4096-byte long chunk from the buffer input
is retrieved by the
chunk, input = input[:lim], input[lim:]
statement in line 77 and dumped into the chunk
array slice, while simultaneously erasing the data from the original input
buffer. Since Go's array slices are just lightweight constructs referencing underlying arrays (which won't change in this case), this is actually very efficient.
The loop starting in line 76 repeats until only a remainder with less than 4096 bytes is left in the input
array slice; line 82 writes the rest to the new file outside the For loop, thus completing the copy process.
To allow the user to interrupt the copy process manually if necessary, line 49 defines a keyboard handler for the Q key, which uses StopLoop()
to close the GUI and terminate the program cleanly.
At the end of the copy process, line 84 sends a true value to the done
channel, which causes the main program in line 34 immediately to unblock and proceeds to terminate the program via ui.StopLoop()
.
One thing to be aware of is that entertaining the user during the copy process is expensive: Writing data in 4096-byte blocks slows things down considerably in the case of larger files. And, as noted previously, Listing 2 reads the file contents to be copied into memory in a single action, which may not be a good idea for gigabyte-sized masses of data. But it's definitely good enough for illustrative purposes, such as for Hollywood's film industry.
Infos
- Progress bar: https://en.wikipedia.org/wiki/Progress_bar
- Listings for this article: ftp://ftp.linux-magazine.com/pub/listings/linux-magazine.com/220/
- "Programming Snapshot – Classics Repackaged: termui" by Mike Schilli, Linux Magazine, issue 218, January 2019, p. 42
- "Programming Snapshot – Simulatenous Runners: goroutines" by Mike Schilli, Linux Magazine, issue 219, February 2019, p. 40
« Previous 1 2
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Linux Servers Targeted by Akira Ransomware
A group of bad actors who have already extorted $42 million have their sights set on the Linux platform.
-
TUXEDO Computers Unveils Linux Laptop Featuring AMD Ryzen CPU
This latest release is the first laptop to include the new CPU from Ryzen and Linux preinstalled.
-
XZ Gets the All-Clear
The back door xz vulnerability has been officially reverted for Fedora 40 and versions 38 and 39 were never affected.
-
Canonical Collaborates with Qualcomm on New Venture
This new joint effort is geared toward bringing Ubuntu and Ubuntu Core to Qualcomm-powered devices.
-
Kodi 21.0 Open-Source Entertainment Hub Released
After a year of development, the award-winning Kodi cross-platform, media center software is now available with many new additions and improvements.
-
Linux Usage Increases in Two Key Areas
If market share is your thing, you'll be happy to know that Linux is on the rise in two areas that, if they keep climbing, could have serious meaning for Linux's future.
-
Vulnerability Discovered in xz Libraries
An urgent alert for Fedora 40 has been posted and users should pay attention.
-
Canonical Bumps LTS Support to 12 years
If you're worried that your Ubuntu LTS release won't be supported long enough to last, Canonical has a surprise for you in the form of 12 years of security coverage.
-
Fedora 40 Beta Released Soon
With the official release of Fedora 40 coming in April, it's almost time to download the beta and see what's new.
-
New Pentesting Distribution to Compete with Kali Linux
SnoopGod is now available for your testing needs