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
-
Thousands of Linux Servers Infected with Stealth Malware Since 2021
Perfctl is capable of remaining undetected, which makes it dangerous and hard to mitigate.
-
Halcyon Creates Anti-Ransomware Protection for Linux
As more Linux systems are targeted by ransomware, Halcyon is stepping up its protection.
-
Valve and Arch Linux Announce Collaboration
Valve and Arch have come together for two projects that will have a serious impact on the Linux distribution.
-
Hacker Successfully Runs Linux on a CPU from the Early ‘70s
From the office of "Look what I can do," Dmitry Grinberg was able to get Linux running on a processor that was created in 1971.
-
OSI and LPI Form Strategic Alliance
With a goal of strengthening Linux and open source communities, this new alliance aims to nurture the growth of more highly skilled professionals.
-
Fedora 41 Beta Available with Some Interesting Additions
If you're a Fedora fan, you'll be excited to hear the beta version of the latest release is now available for testing and includes plenty of updates.
-
AlmaLinux Unveils New Hardware Certification Process
The AlmaLinux Hardware Certification Program run by the Certification Special Interest Group (SIG) aims to ensure seamless compatibility between AlmaLinux and a wide range of hardware configurations.
-
Wind River Introduces eLxr Pro Linux Solution
eLxr Pro offers an end-to-end Linux solution backed by expert commercial support.
-
Juno Tab 3 Launches with Ubuntu 24.04
Anyone looking for a full-blown Linux tablet need look no further. Juno has released the Tab 3.
-
New KDE Slimbook Plasma Available for Preorder
Powered by an AMD Ryzen CPU, the latest KDE Slimbook laptop is powerful enough for local AI tasks.