GoSuda

GUI:n luominen Tk:lla Go-kielellä

By Yunjin Lee
views ...

Pythonissa on sisäänrakennettuna GUI-kirjastoja, kuten Tkinter. Viime aikoina Go-kielelle on kehitetty CGo-Free, Cross Platform Tk -kirjasto, joka mahdollistaa Tcl/Tk:n käytön. Tänään tarkastelemme sen peruskäyttöä.

"Hello, Tk" -sovelluksen luominen

Aloitamme yksinkertaisella 'Hello, TK!' -esimerkillä.

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


Tarkastelemme nyt yksityiskohtaisemmin yllä olevaa esimerkkikoodia ja sen suoritustulosta.

Jos olet aiemmin käyttänyt Pythonin Tk:ta, ymmärrät rakenteen, jossa widgetit pakataan ikkunan sisälle tai suoraan ikkunan alle. Widgetin tyypistä riippuen siihen sisältyy esimerkiksi Labels.

Ipadx ja Ipady ovat lyhenteitä sanoista Internal padding, ja ne säätävät sisäisten widgettien marginaaleja. Tässä esimerkissä säädetään painikkeen marginaaleja.

Tässä kirjastossa on Window-rakenne, ja App-muuttuja hallinnoi ylintä ikkunaa. Tämä on määritelty etukäteen kirjaston sisällä. Siksi tk.App.Wait() -funktion lopettava tk.App.Destroy() -funktio vastaa ylimmän ikkunan sulkemisesta.

Tarkastellaan nyt joitakin esimerkkejä GitLabin _examples-kansiosta.

SVG-tiedostojen käsittely

Seuraavaksi esimerkki SVG-tiedoston näyttämisestä Label-widgetissä.

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

Tässä kirjastossa SVG:n käsittely tapahtuu seuraavasti:

  1. SVG-tiedoston sisältö luetaan merkkijonoksi (tai sisällytetään suoraan kuten yllä olevassa esimerkissä).
  2. Tämä sisältö välitetään Data-funktiolle, joka muuntaa sen merkkijonoksi, joka sisältää Options (-data Option).
  3. Muunnettu bytedata välitetään NewPhoto-funktiolle, joka palauttaa Img-rakenteen osoittimen, joka edustaa Tcl/Tk-kuvaa.
  4. Image-funktion kautta Img-rakenteen osoitin muunnetaan merkkijonoksi, johon on lisätty -Image Option.
  5. Muuntamisen syy rakenteen RAW-arvot sisältäväksi merkkijonoksi on Label-widgetin luominen.

ICO- ja PNG-tiedostoja käsitellään samalla tavalla.

PNG-tiedostojen käsittely

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

PNG-tiedoston käsittelyprosessi on seuraava:

  1. Upotettu gopher.png muunnetaan merkkijonotyypiksi, joka sisältää Options.
  2. NewPhoto-funktion kautta se muunnetaan *Img-tyypiksi.
  3. Image-funktion kautta se muunnetaan RAW-merkkijonoksi ja luodaan sitten Label-widgetiksi.

ICO-tiedostoja käsitellään samalla tavalla, ja ero SVG-muotoon on vain Data-funktion sisäisessä käsittelytavassa.

Tarkastellaan nyt "Options sisältävän merkkijonon" luonnetta:

1type rawOption string

Aiemmin mainittu Options sisältävä merkkijono on pelkkä muotoiltu merkkijono.

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

optionString-metodi on Window-osoittimen metodi, joka palauttaa merkkijonon.

Viimeiseksi tarkastellaan lyhyesti Data-funktion sisäistä rakennetta:

 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}

Koodista näkyy, että ICO- tai PNG-tiedostojen tapauksessa tarvitaan koodaus/dekoodausprosessi. Muissa tapauksissa Data-funktion tulokseksi merkitään vain -data Option lisäämällä merkkijonoon, joka on muunnettu bytetiedoksi ilman erityistä muunnosta.

Kuvien lataaminen valikkowidgetin kautta

Lisäämällä valikkowidgetin aiemmin harjoiteltuihin PNG- ja ICO-latausesimerkkeihin voimme luoda sovelluksen, joka näyttää tarvittavat kuvat.

Tarkastellaan ensin yksinkertaista valikkowidget-esimerkkiä.

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

Tässä esimerkissä olemme luoneet valikkopalkin ja valikon, korostaneet tekstiä, käyttäneet Command-Optionia, sitoneet pikanäppäimiä ja asettaneet sovelluksen ikkunan alkuperäisen koon.

Nyt muokkaamme GetOpenFile-määritettyä Command-funktiota luodaksemme ohjelman, joka lataa ja näyttää kuvia.

Ohjelman laatimissuunnitelma on seuraava:

  1. Rajoita vain PNG- ja ICO-tiedostojen avaaminen
  2. Käsittele tiedostovalintaikkunasta valittu tiedosto
  3. Toteuta widget kuvan näyttämiseen

Seuraava koodi heijastaa tätä suunnitelmaa:

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

Yllä oleva koodi toimii seuraavasti:

  1. Strings-paketin merkkijonovertailufunktiolla tarkistetaan tiedoston laajennus.
  2. Os-pakettia käytetään tiedoston avaamiseen, lukemiseen ja sulkemiseen.
  3. Luettu kuva näytetään Label-widgetinä.

Yhteenveto

Tässä artikkelissa käsiteltiin seuraavia asioita Go-kielen Tcl/Tk-kirjaston avulla:

  1. GUI-sovelluksen perusrakenne
  2. Erilaisten kuvamuotojen, kuten SVG, PNG ja ICO, käsittely
  3. Widgettien pakkaus ja asettelun hallinta
  4. Kuvadatan käsittelyrakenne
  5. Pikanäppäinten sitominen ja widget-komennot

Käyttämällä Go-kieltä ja Tcl/Tk:ta yhdessä voidaan luoda yksinkertaisia mutta käytännöllisiä GUI-ohjelmia. Tämän pohjalta voit yrittää kehittää monimutkaisempia GUI-sovelluksia.