GoSuda

Go'da Tk ile GUI Oluşturma

By Yunjin Lee
views ...

Python'da Tkinter gibi GUI kütüphaneleri varsayılan olarak yerleşiktir. Yakın zamanda Go dilinde de Tcl/Tk kullanabilmek için CGo-Free, Cross Platform Tk kütüphanesi geliştirilmiştir. Bugün bu kütüphanenin temel kullanımını inceleyeceğiz.

Hello, Tk Oluşturma

Öncelikle basit bir 'Hello, TK!' örneği ile başlayalım.

 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}

hello-tk yürütme sonucu


Yukarıdaki örnek kodu ve yürütme sonuçlarını detaylıca inceleyelim.

Python'ın Tk'sını kullanmış olanlar, pencere içine widget'ların paketlendiği veya pencerenin altına doğrudan widget'ların paketlendiği yapıyı anlayacaklardır. Widget türüne göre etiketler vb. içinde yer alır.

Ipadx ve Ipady, Internal padding'in kısaltması olup, dahili widget'ların boşluklarını ayarlar. Bu örnekte düğmenin boşlukları ayarlanır.

Bu kütüphanede bir Window yapı birimi bulunmaktadır ve App adında bir değişken en üst düzey pencereyi yönetir. Bu, kütüphane içinde önceden tanımlanmıştır. Bu nedenle, tk.App.Wait() işlevini sonlandıran tk.App.Destroy() işlevi, en üst düzey pencereyi kapatma görevini üstlenir.

Şimdi GitLab'ın _examples klasöründeki bazı örnekleri inceleyelim.

SVG Dosyalarını İşleme

Aşağıda SVG dosyasını etiket widget'ında görüntüleyen bir örnek verilmiştir.

 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}

go-tk-svg yürütme sonucu

Bu kütüphanede SVG'yi işleme yöntemi aşağıdaki gibidir:

  1. SVG dosyasının içeriği bir string olarak okunur (veya yukarıdaki örnekte olduğu gibi doğrudan eklenir).
  2. Bu içerik, Data işlevine aktarılarak seçenekler içeren bir string'e dönüştürülür (-data seçeneği).
  3. Dönüştürülen bayt değeri, NewPhoto işlevine aktarılarak Tcl/Tk resmini temsil eden bir Img yapı birimi işaretçisi döndürülür.
  4. Image işlevi aracılığıyla Img yapı birimi işaretçisi, -Image seçeneği eklenmiş bir string'e dönüştürülür.
  5. Yapı birimi RAW değerini içeren bir string'e dönüştürülmesinin nedeni, Label widget'ı oluşturulması içindir.

ICO ve PNG dosyaları da benzer şekilde işlenir.

PNG Dosyalarını İşleme

 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}

go-tk-png yürütme sonucu

PNG dosyasını işleme süreci aşağıdaki gibidir:

  1. Gömülü gopher.png, seçenekler içeren bir string tipine dönüştürülür.
  2. NewPhoto işlevi aracılığıyla *Img tipine dönüştürülür.
  3. Image işlevinden geçerek RAW string'e dönüştürüldükten sonra, bir etiket widget'ı olarak oluşturulur.

ICO dosyası da aynı şekilde işlenir; SVG formatıyla tek farkı, Data işlevinin dahili işleme yöntemidir.

Burada "seçenekleri içeren string"in ne olduğunu inceleyelim:

1type rawOption string

Daha önce bahsedilen seçenekleri içeren string, yalnızca biçimlendirilmiş bir stringden ibarettir.

1func (w *Window) optionString(_ *Window) string {
2    return w.String()
3}

optionString yöntemi, Window işaretçisi için bir yöntem olup, bir string döndürür.

Son olarak, Data işlevinin iç yapısını kısaca inceleyelim:

 1func Data(val any) Opt {
 2    switch x := val.(type) {
 3    case []byte:
 4        switch {
 5        case bytes.HasPrefix(x, pngSig):
 6            // Tamam (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}

Koda bakıldığında, ICO veya PNG dosyaları için kodlama/kod çözme sürecinin gerekli olduğu görülmektedir. Diğer durumlarda, özel bir dönüşüm olmaksızın, bayt tipine dönüştürülmüş string'e sadece -data seçeneği eklenerek Data işlevinin çıktısı olduğu belirtilir.

Menü Widget'ı ile Resim Yükleme

Daha önce pratik ettiğimiz PNG, ICO yükleme örneklerine menü widget'ı ekleyerek gerekli resimleri görüntüleyen bir uygulama oluşturabiliriz.

Öncelikle basit bir menü widget'ı örneğini inceleyelim.

 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}

go-tk-menü çubuğu-yürütme sonucu

Bu örnekte menü çubuğu ve menü oluşturma, yazı vurgulama, Command seçeneği, kısayol tuşu bağlama ve uygulama penceresinin başlangıç boyutunu ayarlama işlemleri yapılmıştır.

Şimdi GetOpenFile ile belirtilen Command işlevini düzenleyerek resimleri yükleyen ve görüntüleyen bir program oluşturalım.

Program yazma planı aşağıdaki gibidir:

  1. Yalnızca PNG ve ICO dosyalarını açmakla sınırlandırma
  2. Dosya iletişim kutusundan seçilen dosyayı işleme
  3. Görüntü gösterimi için widget uygulama

Aşağıda bu planı yansıtan kod bulunmaktadır:

 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}

go-tk-resim-yükleme-yürütme sonucu

Yukarıdaki kod aşağıdaki şekilde çalışmaktadır:

  1. strings paketinin string karşılaştırma işlevi ile dosya uzantısı kontrol edilir.
  2. os paketi kullanılarak dosya açılır, okunur ve kapatılır.
  3. Okunan resim, bir etiket widget'ı olarak görüntülenir.

Özet

Bu makalede, Go dilinin Tcl/Tk kütüphanesi kullanılarak aşağıdaki konular ele alınmıştır:

  1. GUI uygulamasının temel yapısı
  2. SVG, PNG, ICO gibi çeşitli görüntü formatlarının işlenmesi
  3. Widget paketleme ve düzen yönetimi
  4. Görüntü veri işleme yapısı
  5. Kısayol tuşu bağlama ve widget komutları

Go dili ve Tcl/Tk'yı birlikte kullanarak basit ancak pratik GUI programları oluşturulabilir. Bu temelde daha karmaşık GUI uygulamaları geliştirmeye meydan okumanızı tavsiye ederiz.