GoSuda

Go ile Tk'de GUI Oluşturma

By Yunjin Lee
views ...

Python'da Tkinter gibi GUI kütüphaneleri varsayılan olarak yerleşik halde bulunur. Yakın zamanda Go dilinde de Tcl/Tk'nin kullanılabilmesi için CGo-Free, Cross Platform Tk kütüphanesi geliştirilmiştir. Bugün bunun temel kullanımına bir göz atacağız.

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!"), // "Hello, TK!" metni içeren bir düğme oluşturur.
 9            tk.Command(func() { // Düğmeye tıklanınca çalışacak komut.
10                tk.Destroy(tk.App) // Uygulamayı kapatır.
11            })),
12        tk.Ipadx(10), tk.Ipady(5), tk.Padx(15), tk.Pady(10), // Düğmenin iç ve dış boşluklarını ayarlar.
13    )
14    tk.App.Wait() // Uygulamanın kapanmasını bekler.
15}

hello-tk 실행 결과


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

Python'ın Tk'sini kullanma deneyimi olanlar, pencere içine widget'ların paketlendiği veya pencerenin alt kısmına doğrudan widget'ların paketlendiği yapıyı anlayacaklardır. Widget türüne göre etiketler vb. içine dahil edilir.

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

Bu kütüphanede bir Window yapısı bulunur 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. Dolayısıyla tk.App.Wait()'i sonlandıran tk.App.Destroy() fonksiyonu, en üst düzey pencereyi kapatma görevini üstlenir.

Şimdi GitLab'ın _examples klasöründeki bazı örneklere göz atalım.

SVG Dosyasını İşleme

Aşağıda SVG dosyasını etiket widget'ında gösteren 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)))), // SVG verisinden bir fotoğraf oluşturur ve bunu bir etiket widget'ına yerleştirir.
20        TExit(), // Uygulamayı kapatma düğmesi.
21        Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m")) // Widget'ın boşluklarını ayarlar.
22    App.Center().Wait() // Uygulama penceresini ortalar ve kapanmasını bekler.
23}

go-tk-svg 실행 결과

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

  1. SVG dosyasının içeriği bir dizge olarak okunur (veya yukarıdaki örnekte olduğu gibi doğrudan dahil edilir).
  2. Bu içerik, Data fonksiyonuna iletilerek seçenekleri içeren bir dizgeye dönüştürülür (-data seçeneği).
  3. Dönüştürülen bayt değeri, NewPhoto fonksiyonuna iletilerek Tcl/Tk görüntüsünü temsil eden bir Img yapı işaretçisi döndürülür.
  4. Image fonksiyonundan geçerken Img yapı işaretçisi, -Image seçeneği eklenmiş bir dizgeye dönüştürülür.
  5. Yapı RAW değerini içeren bir dizgeye dönüştürmenin nedeni, Label widget'ının oluşturulması içindir.

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

PNG Dosyasını İşleme

 1package main
 2
 3import _ "embed"
 4import . "modernc.org/tk9.0"
 5
 6//go:embed gopher.png
 7var gopher []byte // gopher.png dosyasını bayt dilimi olarak gömer.
 8
 9func main() {
10    Pack(Label(Image(NewPhoto(Data(gopher)))), // Gömülü PNG verisinden bir fotoğraf oluşturur ve bunu bir etiket widget'ına yerleştirir.
11        TExit(), // Uygulamayı kapatma düğmesi.
12        Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m")) // Widget'ın boşluklarını ayarlar.
13    App.Center().Wait() // Uygulama penceresini ortalar ve kapanmasını bekler.
14}

go-tk-png 실행결과

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

  1. Gömülü gopher.png, seçenekleri içeren bir dizge türüne dönüştürülür.
  2. NewPhoto fonksiyonu aracılığıyla *Img türüne dönüştürülür.
  3. Image fonksiyonundan geçtikten sonra RAW dizgeye dönüştürülür ve ardından bir etiket widget'ı olarak oluşturulur.

ICO dosyaları da aynı şekilde işlenir; SVG formatı ile tek fark Data fonksiyonunun iç işleyişidir.

Burada "seçenekleri içeren dizge"nin ne olduğuna bakalım:

1type rawOption string

Daha önce bahsedilen seçenekleri içeren dizge, yalnızca biçimlendirilmiş bir dizgeden 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 dizge döndürür.

Son olarak, Data fonksiyonunun iç yapısına kısaca göz atalım:

 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}

Koda bakıldığında, ICO veya PNG dosyaları için kodlama/kod çözme süreci gereklidir. Diğer durumlarda ise, özel bir dönüşüm olmaksızın bayt biçimine dönüştürülmüş dizgeye yalnızca -data seçeneği eklenerek Data fonksiyonunun çıktısı olduğu belirtilir.

Menü Widget'ı ile Görüntü Yükleme

Daha önce pratik yaptığımız PNG, ICO yükleme örneğine bir menü widget'ı ekleyerek gerekli görüntüleri gösteren bir uygulama oluşturabiliriz.

Öncelikle basit bir menü widget'ı örneğine göz atalım.

 1package main
 2
 3import (
 4    "fmt"
 5    . "modernc.org/tk9.0"
 6    "runtime"
 7)
 8
 9func main() {
10    menubar := Menu() // Bir menü çubuğu oluşturur.
11
12    fileMenu := menubar.Menu() // Menü çubuğuna bir "Dosya" menüsü ekler.
13    fileMenu.AddCommand(Lbl("New"), Underline(0), Accelerator("Ctrl+N")) // "Yeni" komutunu ekler.
14    fileMenu.AddCommand(Lbl("Open..."), Underline(0), Accelerator("Ctrl+O"), Command(func() { GetOpenFile() })) // "Aç..." komutunu ekler ve bir dosya açma işlevi tanımlar.
15    Bind(App, "<Control-o>", Command(func() { fileMenu.Invoke(1) })) // Ctrl+O kısayolunu "Aç..." komutuna bağlar.
16    fileMenu.AddCommand(Lbl("Save As..."), Underline(5)) // "Farklı Kaydet..." komutunu ekler.
17    fileMenu.AddSeparator() // Menüye bir ayırıcı ekler.
18    fileMenu.AddCommand(Lbl("Exit"), Underline(1), Accelerator("Ctrl+Q"), ExitHandler()) // "Çıkış" komutunu ekler ve çıkış işlevi tanımlar.
19    Bind(App, "<Control-q>", Command(func() { fileMenu.Invoke(4) })) // Ctrl+Q kısayolunu "Çıkış" komutuna bağlar.
20    menubar.AddCascade(Lbl("File"), Underline(0), Mnu(fileMenu)) // Menü çubuğuna "Dosya" menüsünü ekler.
21
22    App.WmTitle(fmt.Sprintf("%s on %s", App.WmTitle(""), runtime.GOOS)) // Uygulama penceresinin başlığını ayarlar.
23    App.Configure(Mnu(menubar), Width("8c"), Height("6c")).Wait() // Uygulama penceresini yapılandırır ve kapanmasını bekler.
24}

go-tk-메뉴바-실행결과

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 ayarlamayı denedik.

Şimdi GetOpenFile olarak belirlenen Command fonksiyonunu değiştirerek görüntüleri yükleyip gösteren bir program oluşturalım.

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

  1. Yalnızca PNG ve ICO dosyalarının açılmasına izin verilir.
  2. Dosya iletişim kutusundan seçilen dosyayı işleme
  3. Görüntü gösterimi için widget uygulaması

Aşağıda bu planı yansıtan kod verilmiştir:

 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() // Dosya açma iletişim kutusunu gösterir ve seçilen dosyaları alır.
15    for _, photo := range s { // Seçilen her fotoğraf için döngü.
16        formatCheck := strings.Split(photo, ".") // Dosya adını noktaya göre böler.
17        format := formatCheck[len(formatCheck)-1] // Dosya uzantısını alır.
18        
19        if (strings.Compare(format, "png") == 0) || (strings.Compare(format, "ico") == 0) { // Dosya uzantısının "png" veya "ico" olup olmadığını kontrol eder.
20            picFile, err := os.Open(photo) // Fotoğraf dosyasını açar.
21            if err != nil {
22                log.Println("Error while opening photo, error is: ", err) // Hata durumunda log mesajı yazar.
23            }
24
25            pictureRawData := make([]byte, 10000*10000) // Fotoğraf verisi için bir bayt dilimi oluşturur.
26            picFile.Read(pictureRawData) // Fotoğraf dosyasını okur.
27
28            imageLabel := Label(Image(NewPhoto(Data(pictureRawData)))) // Okunan veriden bir görüntü etiketi oluşturur.
29            Pack(imageLabel, // Görüntü etiketini paketler.
30                TExit(), // Uygulamayı kapatma düğmesi.
31                Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m")) // Etiketin boşluklarını ayarlar.
32        }
33        picFile.Close() // Fotoğraf dosyasını kapatır.
34    }
35}
36
37func main() {
38    menubar := Menu() // Bir menü çubuğu oluşturur.
39
40    fileMenu := menubar.Menu() // Menü çubuğuna bir "Dosya" menüsü ekler.
41    fileMenu.AddCommand(Lbl("Open..."), Underline(0), Accelerator("Ctrl+O"), Command(handleFileOpen)) // "Aç..." komutunu ekler ve handleFileOpen işlevini atar.
42    Bind(App, "<Control-o>", Command(func() { fileMenu.Invoke(0) })) // Ctrl+O kısayolunu "Aç..." komutuna bağlar.
43    fileMenu.AddCommand(Lbl("Exit"), Underline(1), Accelerator("Ctrl+Q"), ExitHandler()) // "Çıkış" komutunu ekler ve çıkış işlevi tanımlar.
44    Bind(App, "<Control-q>", Command(func() { fileMenu.Invoke(1) })) // Ctrl+Q kısayolunu "Çıkış" komutuna bağlar.
45    menubar.AddCascade(Lbl("File"), Underline(0), Mnu(fileMenu)) // Menü çubuğuna "Dosya" menüsünü ekler.
46
47    App.WmTitle(fmt.Sprintf("%s on %s", App.WmTitle(""), runtime.GOOS)) // Uygulama penceresinin başlığını ayarlar.
48    App.Configure(Mnu(menubar), Width("10c"), Height("10c")).Wait() // Uygulama penceresini yapılandırır ve kapanmasını bekler.
49}

go-tk-이미지-불러오기-실행결과

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

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

Özet

Bu yazıda, Go dilinin Tcl/Tk kütüphanesini kullanarak aşağıdaki konuları ele aldık:

  1. GUI uygulamaları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'yi birlikte kullanarak basit ama pratik GUI programları oluşturmak mümkündür. Bunu temel alarak daha karmaşık GUI uygulamaları geliştirmeyi deneyebilirsiniz.