GoSuda

Crearea unei interfețe grafice de utilizator în Go cu Tk

By Yunjin Lee
views ...

Python include în mod implicit biblioteci GUI precum Tkinter. Recent, o CGo-Free, Cross Platform Tk 라이브러리 a fost dezvoltată pentru a permite utilizarea Tcl/Tk și în limbajul Go. Astăzi, vom examina utilizarea sa fundamentală.

Crearea unui Hello, Tk

Să începem cu un exemplu simplu de „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}

hello-tk 실행 결과


Vom analiza în detaliu codul exemplu de mai sus și rezultatele execuției.

Dacă aveți experiență cu Tk în Python, veți înțelege structura în care widget-urile sunt împachetate într-o fereastră sau direct sub o fereastră. În funcție de tipul widget-ului, etichete și alte elemente pot fi incluse în acesta.

Ipadx și Ipady sunt acronime pentru Internal padding, ajustând marjele widget-urilor interne. În acest exemplu, marjele butonului sunt ajustate.

Această bibliotecă conține o structură Window, iar variabila App gestionează fereastra de nivel superior. Aceasta este predefinită în cadrul bibliotecii. Prin urmare, funcția tk.App.Destroy(), care închide tk.App.Wait(), are rolul de a închide fereastra de nivel superior.

Acum, vom examina câteva exemple din folderul _examples al GitLab.

Procesarea fișierelor SVG

Urmează un exemplu de afișare a unui fișier SVG într-un 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}

go-tk-svg 실행 결과

Metoda de procesare a SVG în această bibliotecă este următoarea:

  1. Se citește conținutul fișierului SVG ca șir de caractere (sau se include direct, așa cum este exemplificat mai sus).
  2. Acest conținut este transmis funcției Data pentru a fi convertit într-un șir de caractere cu opțiuni (opțiunea -data).
  3. Valorile de octeți convertite sunt transmise funcției NewPhoto, care returnează un pointer la o structură Img ce reprezintă o imagine Tcl/Tk.
  4. Prin trecerea prin funcția Image, pointerul la structura Img este convertit într-un șir de caractere cu opțiunea -Image adăugată.
  5. Motivul conversiei în șir de caractere care conține valorile RAW ale structurii este crearea widget-ului Label.

Fișierele ICO și PNG sunt procesate într-o manieră similară.

Procesarea fișierelor 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}

go-tk-png 실행결과

Procesul de prelucrare a fișierelor PNG este următorul:

  1. Fișierul gopher.png încorporat este convertit într-un șir de caractere care include opțiuni.
  2. Este convertit la tipul *Img prin funcția NewPhoto.
  3. După ce este convertit într-un șir de caractere RAW prin funcția Image, este creat ca un widget de etichetă.

Fișierele ICO sunt procesate în același mod, iar singura diferență față de formatul SVG este modul de prelucrare internă a funcției Data.

Acum, vom examina natura „șirului de caractere cu opțiuni”:

1type rawOption string

Șirul de caractere cu opțiuni menționat anterior este pur și simplu un șir de caractere formatat.

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

Metoda optionString este o metodă pentru un pointer Window, care returnează un șir de caractere.

În cele din urmă, vom examina pe scurt structura internă a funcției 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}

Din cod, se observă că fișierele ICO sau PNG necesită procese de codificare/decodificare. În alte cazuri, fără o conversie specială, se adaugă doar opțiunea -data la șirul de caractere convertit în format de octeți, pentru a indica faptul că este rezultatul funcției Data.

Încărcarea imaginilor cu widget-ul Menu

Dacă adăugăm un widget de meniu la exemplele anterioare de încărcare PNG și ICO, putem crea o aplicație care afișează imaginile necesare.

Mai întâi, vom examina un exemplu simplu de widget de meniu.

 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-메뉴바-실행결과

În acest exemplu, am configurat o bară de meniu și un meniu, am subliniat textul, am utilizat opțiunea Command, am legat taste rapide și am setat dimensiunea inițială a ferestrei aplicației.

Acum, vom modifica funcția Command, denumită GetOpenFile, pentru a crea un program care încarcă și afișează imagini.

Planul de dezvoltare a programului este următorul:

  1. Restricționarea deschiderii doar a fișierelor PNG și ICO.
  2. Procesarea fișierului selectat din dialogul de fișiere.
  3. Implementarea unui widget pentru afișarea imaginilor.

Următorul cod reflectă acest plan:

 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-이미지-불러오기-실행결과

Codul de mai sus funcționează în felul următor:

  1. Verifică extensia fișierului folosind funcția de comparare a șirurilor de caractere din pachetul strings.
  2. Deschide, citește și închide fișierul folosind pachetul os.
  3. Imaginea citită este afișată ca un widget de etichetă.

Rezumat

În acest articol, am utilizat biblioteca Tcl/Tk din limbajul Go pentru a acoperi următoarele subiecte:

  1. Structura fundamentală a aplicațiilor GUI.
  2. Procesarea diverselor formate de imagine, inclusiv SVG, PNG și ICO.
  3. Gestionarea împachetării widget-urilor și a aspectului.
  4. Structura de procesare a datelor imaginii.
  5. Legarea tastelor rapide și a comenzilor widget-urilor.

Utilizând limbajul Go și Tcl/Tk împreună, puteți crea programe GUI simple, dar practice. Pe baza acestora, vă încurajăm să abordați dezvoltarea de aplicații GUI mai complexe.