Membangun GUI dengan Tk di Go
Python secara bawaan memiliki 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 membahas penggunaan dasarnya.
Membuat Hello, Tk
Mari kita mulai 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 tinjau lebih lanjut contoh kode dan hasil eksekusi di atas.
Bagi mereka yang memiliki pengalaman menggunakan Tk di Python, Anda akan memahami struktur di mana widget dipaketkan di dalam jendela atau langsung di bawah jendela. Bergantung pada jenis widget, label, dan lainnya akan disertakan di dalamnya.
Ipadx dan Ipady adalah singkatan dari Internal padding, yang mengatur margin internal widget. Dalam contoh ini, margin tombol disesuaikan.
Pustaka ini memiliki struktur Window, dan variabel App mengelola jendela tingkat atas. Ini sudah ditentukan 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 lihat beberapa contoh yang ada di folder _examples GitLab.
Memproses Berkas SVG
Berikut adalah contoh 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}
Cara pustaka ini memproses SVG adalah sebagai berikut:
- Membaca konten berkas SVG sebagai string (atau menyertakannya secara langsung seperti contoh di atas).
- Meneruskan konten ini ke fungsi Data untuk mengubahnya menjadi string yang berisi opsi (opsi -data).
- 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 ditambahkan.
- Alasan konversi ke string yang berisi nilai RAW struktur adalah untuk membuat 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:
- Mengonversi gopher.png yang disematkan menjadi tipe string yang berisi opsi.
- Mengonversinya menjadi tipe *Img melalui fungsi NewPhoto.
- Setelah melewati fungsi Image dan diubah menjadi string RAW, ia akan dibuat sebagai widget label.
Berkas ICO juga diproses dengan cara yang sama, dengan satu-satunya perbedaan dari format SVG adalah cara penanganan internal fungsi Data.
Mari kita lihat "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, dan mengembalikan string.
Terakhir, mari kita lihat 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}
Jika kita melihat kodenya, dalam kasus berkas ICO atau PNG, diperlukan proses pengkodean/dekode. Dalam kasus lain, hanya opsi -data yang ditambahkan ke string yang dikonversi menjadi tipe byte tanpa konversi khusus, 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 lihat 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, menyoroti teks, opsi Command, mengikat pintasan keyboard, dan mengatur ukuran awal jendela aplikasi.
Sekarang, mari kita ubah fungsi Command yang ditentukan sebagai GetOpenFile untuk membuat program yang memuat dan menampilkan gambar.
Rencana penulisan program adalah sebagai berikut:
- Batasi pembukaan hanya untuk berkas PNG dan ICO
- Memproses berkas yang dipilih dari dialog berkas
- Mengimplementasikan widget untuk menampilkan gambar
Berikut adalah kode yang mencerminkan rencana ini:
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 berfungsi dengan cara berikut:
- Memeriksa ekstensi berkas dengan fungsi perbandingan string dari paket strings.
- Membuka, membaca, dan menutup berkas menggunakan paket os.
- Gambar yang dibaca ditampilkan sebagai widget label.
Ringkasan
Dalam tulisan ini, kita 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
- Pengelolaan paket widget dan tata letak
- Struktur pemrosesan data gambar
- Pengikatan pintasan keyboard dan perintah widget
Dengan menggunakan bahasa Go dan Tcl/Tk bersama-sama, Anda dapat membuat program GUI yang sederhana namun praktis. Berdasarkan hal ini, tantang diri Anda untuk mengembangkan aplikasi GUI yang lebih kompleks.