Membuat GUI dengan Tk di Go
Python secara bawaan menyertakan pustaka GUI seperti Tkinter. Baru-baru ini, sebuah Pustaka Tk CGo-Free, Cross Platform telah dikembangkan agar Tcl/Tk dapat digunakan dalam bahasa Go. Hari ini, kita akan mengamati penggunaan dasarnya.
Membuat Hello, Tk
Pertama, kita akan memulai dengan contoh sederhana 'Hello, TK!'.
1package main
2
3import tk "modernc.org/tk9.0"
4
5func main() {
6 tk.Pack(
7 tk.TButton(
8 tk.Txt("Hello, TK!"),
9 tk.Command(func() {
10 tk.Destroy(tk.App)
11 })),
12 tk.Ipadx(10), tk.Ipady(5), tk.Padx(15), tk.Pady(10),
13 )
14 tk.App.Wait()
15}

Mari kita periksa secara cermat kode contoh di atas dan hasil eksekusinya.
Bagi mereka yang memiliki pengalaman menggunakan Tk di Python, Anda akan memahami struktur di mana widget dikemas di dalam jendela atau langsung di bawah jendela. Bergantung pada jenis widget, label, dan elemen lain akan disertakan di dalamnya.
Ipadx dan Ipady adalah singkatan dari Internal padding, yang menyesuaikan margin widget internal. Dalam contoh ini, margin tombol disesuaikan.
Pustaka ini memiliki struktur Window, dan variabel App mengelola jendela tingkat atas. Ini telah didefinisikan sebelumnya di dalam pustaka. Oleh karena itu, fungsi tk.App.Destroy() yang mengakhiri tk.App.Wait() berfungsi untuk menutup jendela tingkat atas.
Sekarang, mari kita periksa beberapa contoh yang terdapat di folder _examples GitLab.
Memproses Berkas SVG
Berikut adalah contoh yang menampilkan berkas SVG pada widget label.
1package main
2
3import . "modernc.org/tk9.0"
4
5// https://en.wikipedia.org/wiki/SVG
6const svg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
7<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
8<svg width="391" height="391" viewBox="-70.5 -70.5 391 391" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9<rect fill="#fff" stroke="#000" x="-70" y="-70" width="390" height="390"/>
10<g opacity="0.8">
11 <rect x="25" y="25" width="200" height="200" fill="lime" stroke-width="4" stroke="pink" />
12 <circle cx="125" cy="125" r="75" fill="orange" />
13 <polyline points="50,150 50,200 200,200 200,100" stroke="red" stroke-width="4" fill="none" />
14 <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" />
15</g>
16</svg>`
17
18func main() {
19 Pack(Label(Image(NewPhoto(Data(svg)))),
20 TExit(),
21 Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m"))
22 App.Center().Wait()
23}

Metode pemrosesan SVG dalam pustaka ini adalah sebagai berikut:
- Konten berkas SVG dibaca sebagai string (atau disertakan secara langsung seperti contoh di atas).
- Konten ini diteruskan ke fungsi Data untuk dikonversi menjadi string yang berisi opsi (-data option).
- Nilai byte yang dikonversi diteruskan ke fungsi NewPhoto, yang mengembalikan pointer struktur Img yang merepresentasikan gambar Tcl/Tk.
- Saat melewati fungsi Image, pointer struktur Img dikonversi menjadi string dengan opsi -Image yang ditambahkan.
- Alasan konversi ke string yang berisi nilai RAW struktur adalah untuk pembuatan widget Label.
Berkas ICO dan PNG juga diproses dengan cara yang serupa.
Memproses Berkas PNG
1package main
2
3import _ "embed"
4import . "modernc.org/tk9.0"
5
6//go:embed gopher.png
7var gopher []byte
8
9func main() {
10 Pack(Label(Image(NewPhoto(Data(gopher)))),
11 TExit(),
12 Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m"))
13 App.Center().Wait()
14}

Proses penanganan berkas PNG adalah sebagai berikut:
- gopher.png yang disematkan dikonversi menjadi tipe string yang berisi opsi.
- Dikonversi menjadi tipe *Img melalui fungsi NewPhoto.
- Setelah dikonversi menjadi string RAW melalui fungsi Image, kemudian dibuat sebagai widget label.
Berkas ICO diproses dengan cara yang sama, dan perbedaan dengan format SVG hanya terletak pada metode pemrosesan internal fungsi Data.
Mari kita periksa "string yang berisi opsi" di sini:
1type rawOption string
String yang berisi opsi yang disebutkan sebelumnya hanyalah string yang diformat.
1func (w *Window) optionString(_ *Window) string {
2 return w.String()
3}
Metode optionString adalah metode untuk pointer Window, yang mengembalikan string.
Terakhir, mari kita periksa secara singkat struktur internal fungsi Data:
1func Data(val any) Opt {
2 switch x := val.(type) {
3 case []byte:
4 switch {
5 case bytes.HasPrefix(x, pngSig):
6 // ok
7 case bytes.HasPrefix(x, icoSig):
8 b := bytes.NewBuffer(x)
9 img, err := ico.Decode(bytes.NewReader(x))
10 if err != nil {
11 fail(err)
12 return rawOption("")
13 }
14
15 b.Reset()
16 if err := png.Encode(b, img); err != nil {
17 fail(err)
18 return rawOption("")
19 }
20
21 val = b.Bytes()
22 }
23 }
24 return rawOption(fmt.Sprintf(`-data %s`, optionString(val)))
25}
Dari kode tersebut, terlihat bahwa berkas ICO atau PNG memerlukan proses encoding/decoding. Dalam kasus lain, tanpa konversi khusus, hanya opsi -data yang ditambahkan ke string yang dikonversi menjadi tipe byte untuk menunjukkan bahwa itu adalah hasil dari fungsi Data.
Memuat Gambar dengan Widget Menu
Dengan menambahkan widget menu ke contoh pemuatan PNG dan ICO yang telah kita praktikkan sebelumnya, kita dapat membuat aplikasi yang menampilkan gambar yang diperlukan.
Pertama, mari kita periksa contoh widget menu sederhana.
1package main
2
3import (
4 "fmt"
5 . "modernc.org/tk9.0"
6 "runtime"
7)
8
9func main() {
10 menubar := Menu()
11
12 fileMenu := menubar.Menu()
13 fileMenu.AddCommand(Lbl("New"), Underline(0), Accelerator("Ctrl+N"))
14 fileMenu.AddCommand(Lbl("Open..."), Underline(0), Accelerator("Ctrl+O"), Command(func() { GetOpenFile() }))
15 Bind(App, "<Control-o>", Command(func() { fileMenu.Invoke(1) }))
16 fileMenu.AddCommand(Lbl("Save As..."), Underline(5))
17 fileMenu.AddSeparator()
18 fileMenu.AddCommand(Lbl("Exit"), Underline(1), Accelerator("Ctrl+Q"), ExitHandler())
19 Bind(App, "<Control-q>", Command(func() { fileMenu.Invoke(4) }))
20 menubar.AddCascade(Lbl("File"), Underline(0), Mnu(fileMenu))
21
22 App.WmTitle(fmt.Sprintf("%s on %s", App.WmTitle(""), runtime.GOOS))
23 App.Configure(Mnu(menubar), Width("8c"), Height("6c")).Wait()
24}

Dalam contoh ini, kita telah mencoba membuat bilah menu dan menu, menyorot teks, opsi Command, pengikatan pintasan, dan mengatur ukuran awal jendela aplikasi.
Sekarang, mari kita ubah fungsi Command yang ditentukan oleh GetOpenFile untuk membuat program yang memuat dan menampilkan gambar.
Rencana penulisan program adalah sebagai berikut:
- Membatasi hanya berkas PNG dan ICO yang dapat dibuka
- Memproses berkas yang dipilih dari dialog berkas
- Mengimplementasikan widget untuk menampilkan gambar
Berikut adalah kode yang mencerminkan rencana-rencana tersebut:
1package main
2
3import (
4 "fmt"
5 "log"
6 "os"
7 "runtime"
8 "strings"
9
10 . "modernc.org/tk9.0"
11)
12
13func handleFileOpen() {
14 s := GetOpenFile()
15 for _, photo := range s {
16 formatCheck := strings.Split(photo, ".")
17 format := formatCheck[len(formatCheck)-1]
18
19 if (strings.Compare(format, "png") == 0) || (strings.Compare(format, "ico") == 0) {
20 picFile, err := os.Open(photo)
21 if err != nil {
22 log.Println("Error while opening photo, error is: ", err)
23 }
24
25 pictureRawData := make([]byte, 10000*10000)
26 picFile.Read(pictureRawData)
27
28 imageLabel := Label(Image(NewPhoto(Data(pictureRawData))))
29 Pack(imageLabel,
30 TExit(),
31 Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m"))
32 }
33 picFile.Close()
34 }
35}
36
37func main() {
38 menubar := Menu()
39
40 fileMenu := menubar.Menu()
41 fileMenu.AddCommand(Lbl("Open..."), Underline(0), Accelerator("Ctrl+O"), Command(handleFileOpen))
42 Bind(App, "<Control-o>", Command(func() { fileMenu.Invoke(0) }))
43 fileMenu.AddCommand(Lbl("Exit"), Underline(1), Accelerator("Ctrl+Q"), ExitHandler())
44 Bind(App, "<Control-q>", Command(func() { fileMenu.Invoke(1) }))
45 menubar.AddCascade(Lbl("File"), Underline(0), Mnu(fileMenu))
46
47 App.WmTitle(fmt.Sprintf("%s on %s", App.WmTitle(""), runtime.GOOS))
48 App.Configure(Mnu(menubar), Width("10c"), Height("10c")).Wait()
49}

Kode di atas beroperasi dengan cara sebagai berikut:
- Fungsi perbandingan string dari paket strings digunakan untuk memeriksa ekstensi berkas.
- Berkas dibuka, dibaca, dan ditutup menggunakan paket os.
- Gambar yang dibaca ditampilkan sebagai widget label.
Ringkasan
Dalam artikel ini, kami telah membahas hal-hal berikut dengan memanfaatkan pustaka Tcl/Tk dalam bahasa Go:
- Struktur dasar aplikasi GUI
- Pemrosesan berbagai format gambar seperti SVG, PNG, dan ICO
- Pengemasan widget dan manajemen tata letak
- Struktur pemrosesan data gambar
- Pengikatan pintasan dan perintah widget
Dengan menggunakan bahasa Go dan Tcl/Tk secara bersamaan, dimungkinkan untuk membuat program GUI yang sederhana namun praktis. Berdasarkan hal ini, disarankan untuk mencoba mengembangkan aplikasi GUI yang lebih kompleks.