GUI on Golang: GTK + 3
- Tutorial
I decided to write one cross-platform desktop application on Go . Made a CLI version, everything works fine. Moreover, Go Cross-compilation is supported. Everything is fine in general. But I also needed a GUI version. And then it began ...
Library selection (binding) for GUI
The application had to be cross-platform.
Therefore, it should compile under Windows , GNU / Linux and macOS .
The choice fell on such libraries:
- gotk3 (GTK + 3)
- therecipe / qt (Qt)
- zserge / webview (native webview)
Electron and other frameworks that pull Chromium and node.js with them , I threw away as they weigh quite a lot, they also eat up a lot of operating system resources.
Now a little about each library.
gotk3
Binding of the library GTK + 3 . Coverage is not all possibilities, but all the main presence.
Compiled application using the standard go build
. Cross-platform compilation is possible, with the exception of macOS . Only with macOS you can compile for this OS, and with macOS you can also compile under Windows + GNU / Linux .
The interface will appear natively for GNU / Linux , Windows (you will need to specify a special theme). For macOS it will not look native. It is possible to get out only if it is a terrible topic that will emulate the native elements of macOS .
therecipe / qt
Binding the Qt 5 library . Support for QML, standard widgets. In general, many people advise this binding.
Compiled using a special command qtdeploy
. In addition to the desktop platforms, there are also mobile ones. Crosscompilation takes place using Docker . For Apple operating systems , you can only compile with macOS .
At desire on Qt it is possible to achieve that the interface looked natively on desktop OS.
zserge / webview
The library, which was originally written in C , the author has screwed it to many languages, including Go . Native webview is used to display: Windows - MSHTML , GNU / Linux - gtk-webkit2 , macOS - Cocoa / WebKit . In addition to the Go code, you will need to pee on JS , well, HTML is useful.
Compiled with go build
, cross- compilation is possible with xgo .
Looks native can as far as the standard browser allows.
Selection
Why did I choose gotk3 ?
In therecipe / qt, I didn’t like the application’s very complicated build system, they even made a special command.
zserge / webview does not seem to be bad, it will not weigh much, but still this is a webview and there may be standard problems that occur in such applications: something may go somewhere. And this is not Electron , where the advanced Chromium is always bundled , and everything can go to some old Windows . And besides, you also have to write on JS .
gotk3 I chose as something in between. You can build a standard go build
, it looks acceptable, and indeed I love GTK + 3 !
In general, I thought everything would be easy. And that for nothing about Go say that in it a problem with GUI . But how am I wrong ...
Getting started
Install everything from gotk3 ( gtk , gdk , glib , cairo ) to yourself:
go get github.com/gotk3/gotk3/...
Also, your system should have the GTK + 3 library itself in development.
GNU / Linux
In Ubuntu :
sudo apt-get install libgtk-3-dev
In Arch Linux :
sudo pacman -S gtk3
macOS
Via Homebrew :
brew install gtk-mac-integration gtk+3
Windows
Everything is not so simple here. The official instructions suggest using MSYS2 and already do everything in it. Personally, I wrote code on other operating systems, and did cross-compilation for Windows in Arch Linux , which I hope to write soon.
Simple example
Now we write a small file with the code main.go
:
package main
import (
"log""github.com/gotk3/gotk3/gtk"
)
funcmain() {
// Инициализируем GTK.
gtk.Init(nil)
// Создаём окно верхнего уровня, устанавливаем заголовок// И соединяем с сигналом "destroy" чтобы можно было закрыть// приложение при закрытии окна
win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
if err != nil {
log.Fatal("Не удалось создать окно:", err)
}
win.SetTitle("Простой пример")
win.Connect("destroy", func() {
gtk.MainQuit()
})
// Создаём новую метку чтобы показать её в окне
l, err := gtk.LabelNew("Привет, gotk3!")
if err != nil {
log.Fatal("Не удалось создать метку:", err)
}
// Добавляем метку в окно
win.Add(l)
// Устанавливаем размер окна по умолчанию
win.SetDefaultSize(800, 600)
// Отображаем все виджеты в окне
win.ShowAll()
// Выполняем главный цикл GTK (для отрисовки). Он остановится когда// выполнится gtk.MainQuit()
gtk.Main()
}
You can compile using the command go build
, and then run the binary. But we just run it:
go run main.go
After launch, we get a window of this type:
Congratulations! You have a simple app from README gotk3 !
More examples can be found on Github gotk3 . I will not disassemble them. Let's better deal with what is not in the examples!
Glade
There is such a thing for Gtk + 3 - Glade . This is the GUI Designer for GTK + . It looks like this:
In order not to create each element manually and not to place it somewhere in the window with the help of a program code, you can distribute the entire design in Glade . Then save everything to an XML-like * .glade file and load it already through our application.
Install glade
GNU / Linux
It is easy to install the glade in GNU / Linux distributions . In some Ubuntu it will be:
sudo apt-get install glade
In Arch Linux :
sudo pacman -S glade
macOS
In downloads from the official site is very old build. Therefore, it is better to install via Homebrew :
brew install glade
And then run:
glade
Windows
You can download the latest version here . I personally did not install it on Windows at all, so I don’t know about the stability of Glade work there .
Simple application using Glade
In general, I designed a window like this:
Saved and received file main.glade
:
<?xml version="1.0" encoding="UTF-8"?><!-- Generated with glade 3.22.1 --><interface><requireslib="gtk+"version="3.20"/><objectclass="GtkWindow"id="window_main"><propertyname="title"translatable="yes">Пример Glade</property><propertyname="can_focus">False</property><child><placeholder/></child><child><objectclass="GtkBox"><propertyname="visible">True</property><propertyname="can_focus">False</property><propertyname="margin_left">10</property><propertyname="margin_right">10</property><propertyname="margin_top">10</property><propertyname="margin_bottom">10</property><propertyname="orientation">vertical</property><propertyname="spacing">10</property><child><objectclass="GtkEntry"id="entry_1"><propertyname="visible">True</property><propertyname="can_focus">True</property></object><packing><propertyname="expand">False</property><propertyname="fill">True</property><propertyname="position">0</property></packing></child><child><objectclass="GtkButton"id="button_1"><propertyname="label"translatable="yes">Go</property><propertyname="visible">True</property><propertyname="can_focus">True</property><propertyname="receives_default">True</property></object><packing><propertyname="expand">False</property><propertyname="fill">True</property><propertyname="position">1</property></packing></child><child><objectclass="GtkLabel"id="label_1"><propertyname="visible">True</property><propertyname="can_focus">False</property><propertyname="label"translatable="yes">This is label</property></object><packing><propertyname="expand">False</property><propertyname="fill">True</property><propertyname="position">2</property></packing></child></object></child></object></interface>
That is, we got a window window_main
( GtkWindow
) in which inside the container ( GtkBox
), which contains an input field entry_1
( GtkEntry
), a button button_1
( GtkButton
) and a label label_1
( GtkLabel
). In addition, there are still attributes of sampling (I set up a bit), visibility and other attributes that Glade added automatically.
Let's now try to download this presentation in our main.go
:
package main
import (
"log""github.com/gotk3/gotk3/gtk"
)
funcmain() {
// Инициализируем GTK.
gtk.Init(nil)
// Создаём билдер
b, err := gtk.BuilderNew()
if err != nil {
log.Fatal("Ошибка:", err)
}
// Загружаем в билдер окно из файла Glade
err = b.AddFromFile("main.glade")
if err != nil {
log.Fatal("Ошибка:", err)
}
// Получаем объект главного окна по ID
obj, err := b.GetObject("window_main")
if err != nil {
log.Fatal("Ошибка:", err)
}
// Преобразуем из объекта именно окно типа gtk.Window// и соединяем с сигналом "destroy" чтобы можно было закрыть// приложение при закрытии окна
win := obj.(*gtk.Window)
win.Connect("destroy", func() {
gtk.MainQuit()
})
// Отображаем все виджеты в окне
win.ShowAll()
// Выполняем главный цикл GTK (для отрисовки). Он остановится когда// выполнится gtk.MainQuit()
gtk.Main()
}
Run again:
go run main.go
And we get:
Hooray! Now we form submission hold XML -like main.glade
file and code main.go
!
Signals
The window starts up, but let's add interactivity. Let the text from the input field when you click on the button will fall into the label.
To do this, first we get the elements of the input field, the button and the label in the code:
// Получаем поле ввода
obj, _ = b.GetObject("entry_1")
entry1 := obj.(*gtk.Entry)
// Получаем кнопку
obj, _ = b.GetObject("button_1")
button1 := obj.(*gtk.Button)
// Получаем метку
obj, _ = b.GetObject("label_1")
label1 := obj.(*gtk.Label)
I do not handle errors that the function returns GetObject()
in order to make the code more simple. But in a real working application they must be processed.
Good. With the code above, we get our form elements. And now let's process the signal clicked
button (when the button is pressed). The GTK + signal is essentially a reaction to an event. Add the code:
// Сигнал по нажатию на кнопку
button1.Connect("clicked", func() {
text, err := entry1.GetText()
if err == nil {
// Устанавливаем текст из поля ввода метке
label1.SetText(text)
}
})
Now run the code:
go run main.go
After entering some text in the field and clicking on the Go button, we will see this text in the label:
Now we have an interactive application!
Conclusion
At this stage, everything seems simple and does not cause difficulties. But I had difficulties with cross- compilation (after all, gotk3 compiles with CGO ), integration with operating systems and the file selection dialog. I even added a native dialogue to the gotk project . Also in my project needed internationalization. There are some features too. If you are interested to see it all now in code, then you can see here .
The source codes of the examples from the article are here .
And if you want to read the sequel, you can vote. And if it turns out to be interesting to someone, I will continue to write.
Only registered users can participate in the survey. Sign in , please.