GoSuda

Creazione di GUI con Tk in Go

By Yunjin Lee
views ...

Python include librerie GUI come Tkinter come parte integrante. Recentemente, è stata sviluppata una libreria Tk cross-platform CGo-Free per consentire l'utilizzo di Tcl/Tk anche nel linguaggio Go. Oggi esamineremo le sue funzionalità di base.

Creazione di un "Hello, Tk"

Iniziamo con un semplice esempio "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}

Esecuzione di hello-tk


Esaminiamo attentamente il codice di esempio e i risultati dell'esecuzione.

Chiunque abbia esperienza nell'uso di Tk con Python comprenderà la struttura in cui i widget sono impacchettati all'interno di una finestra o direttamente sotto di essa. I label e altri elementi sono inclusi a seconda del tipo di widget.

Ipadx e Ipady sono abbreviazioni di Internal padding e regolano la spaziatura interna dei widget. In questo esempio, viene regolata la spaziatura del pulsante.

Questa libreria include una struttura Window, e una variabile denominata App gestisce la finestra di livello superiore. Questa è predefinita all'interno della libreria. Pertanto, la funzione tk.App.Destroy(), che termina tk.App.Wait(), ha il compito di chiudere la finestra di livello superiore.

Ora esamineremo alcuni esempi presenti nella cartella _examples di GitLab.

Elaborazione di file SVG

Di seguito è riportato un esempio di visualizzazione di un file SVG in 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}

Esecuzione di go-tk-svg

Il metodo per elaborare SVG in questa libreria è il seguente:

  1. Il contenuto del file SVG viene letto come una stringa (o incluso direttamente come nell'esempio precedente).
  2. Questo contenuto viene passato alla funzione Data per essere convertito in una stringa contenente opzioni (opzione -data).
  3. Il valore in byte convertito viene passato alla funzione NewPhoto, che restituisce un puntatore alla struttura Img che rappresenta l'immagine Tcl/Tk.
  4. Passando attraverso la funzione Image, il puntatore alla struttura Img viene convertito in una stringa con l'opzione -Image aggiunta.
  5. Il motivo della conversione in una stringa contenente i valori RAW della struttura è la creazione del widget Label.

I file ICO e PNG sono gestiti in modo simile.

Elaborazione di file 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}

Esecuzione di go-tk-png

Il processo di elaborazione dei file PNG è il seguente:

  1. Il file gopher.png incorporato viene convertito in un tipo di stringa contenente opzioni.
  2. Viene convertito in un tipo *Img tramite la funzione NewPhoto.
  3. Dopo essere stato convertito in una stringa RAW tramite la funzione Image, viene creato come widget Label.

I file ICO sono gestiti allo stesso modo, e la differenza rispetto al formato SVG risiede solo nel modo in cui la funzione Data elabora internamente i dati.

Esaminiamo ora la natura di questa "stringa contenente opzioni":

1type rawOption string

La stringa contenente opzioni menzionata in precedenza è semplicemente una stringa formattata.

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

Il metodo optionString è un metodo per un puntatore Window che restituisce una stringa.

Infine, esaminiamo brevemente la struttura interna della funzione 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}

Dal codice si evince che per i file ICO o PNG è necessario un processo di codifica/decodifica. In altri casi, senza conversioni particolari, viene semplicemente aggiunta l'opzione -data alla stringa convertita in tipo byte per indicare che è il risultato della funzione Data.

Caricamento di immagini con il widget Menu

Aggiungendo un widget Menu agli esempi precedenti di caricamento di PNG e ICO, è possibile creare un'applicazione che visualizza le immagini desiderate.

Iniziamo con un semplice esempio di widget Menu.

 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}

Esecuzione della barra dei menu go-tk

In questo esempio, abbiamo configurato una barra dei menu e la creazione di menu, l'evidenziazione del testo, l'opzione Command, la binding delle scorciatoie da tastiera e le dimensioni iniziali della finestra dell'applicazione.

Ora modificheremo la funzione Command designata come GetOpenFile per creare un programma che carica e visualizza le immagini.

Il piano di sviluppo del programma è il seguente:

  1. Limitare l'apertura ai soli file PNG e ICO.
  2. Elaborare il file selezionato dalla finestra di dialogo file.
  3. Implementare un widget per la visualizzazione delle immagini.

Di seguito è riportato il codice che riflette questo piano:

 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}

Esecuzione del caricamento immagini go-tk

Il codice sopra funziona nel seguente modo:

  1. Controlla l'estensione del file utilizzando le funzioni di confronto stringhe del pacchetto strings.
  2. Utilizza il pacchetto os per aprire, leggere e chiudere il file.
  3. L'immagine letta viene visualizzata come widget Label.

Riepilogo

In questo articolo, abbiamo utilizzato la libreria Tcl/Tk del linguaggio Go per trattare i seguenti argomenti:

  1. Struttura di base delle applicazioni GUI.
  2. Elaborazione di vari formati di immagine come SVG, PNG, ICO.
  3. Gestione dell'impacchettamento dei widget e del layout.
  4. Struttura di elaborazione dei dati immagine.
  5. Binding delle scorciatoie da tastiera e comandi dei widget.

L'utilizzo congiunto di Go e Tcl/Tk consente di creare programmi GUI semplici ma pratici. Sulla base di ciò, vi incoraggiamo a cimentarvi nello sviluppo di applicazioni GUI più complesse.