Graphical updates from goroutines

Communicating with the graphical interface (in most frameworks) means managing threads correctly. In the preceding example, we could have updated a GUI within the dirSize() method, adding a row to a table, for instance. In theory, that would have avoided the need for a channel and the struct that we passed back to the reportUsage() function. However, changing threads is a (relatively) slow process depending on other application activities, and moreover we should try to separate our logic and processing from the user interface code. Doing so will make it easier to reuse code later and possibly to change toolkit if our requirements change.

Our design to handle most of the user interaction in a single function means that our actual directory usage code is completely separate from our user interface. Let's update the preceding example to generate a graphical output instead. We'll use Go-GTK this time, as its thread handling is quite explicit:

func gtkReportUsage(path string, list *gtk.ListStore, totalLabel *gtk.Label) {
f, _ := os.Open(path)
files, _ := f.Readdir(-1)
f.Close()

result := make(chan sizeInfo)
for _, file := range files {
go dirSize(filepath.Join(path, file.Name()), result)
}

var total int64
results := 0
for info := range result {
var listIter gtk.TreeIter
total += info.size

gdk.ThreadsEnter()
list.Append(&listIter)
list.SetValue(&listIter, 0, info.name)
list.SetValue(&listIter, 1, formatSize(info.size))
gdk.ThreadsLeave()

results++
if results == len(files) {
break
}
}

gdk.ThreadsEnter()
totalLabel.SetText(fmt.Sprintf("Total: %s", formatSize(total)))
gdk.ThreadsLeave()
}

Notice that our replacement usage reporting method has two instances of gdk.ThreadsEnter() and gdk.ThreadsLeave(); each time we update the user interface, we must switch to the gdk main thread. As in previous Go-GTK examples, we need to also update the main method to correctly initialize thread handling:

func main() {
glib.ThreadInit(nil)
gdk.ThreadsInit()
gdk.ThreadsEnter()
gtk.Init(nil)

window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)

...

gtk.Main()
}

The full user interface creation is omitted from this chapter for brevity, but can be found in this book's source code (in chapter12/goroutines/gtkdiskusage.go). It's a requirement of most graphical toolkits that background processes switch to the main or graphics thread when updating the user interface. Some, such as Fyne, don't have this requirement, which you can see in an alternative version of the example (also available in this book's code repository at chapter12/goroutines/fynediskusage.go). Instead of wrapping GUI calls in thread handling code, we simply call list.Append() or label.SetText() from the background code and the interface will update accordingly:

A GTK interface for the disk usage example
The same disk usage example using Fyne
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.79.65