GoSuda

GUI:n luominen Tk:lla Go:ssa

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 käyttänyt Pythonin Tk-kirjastoa, ymmärrät rakenteen, jossa widgetit pakataan ikkunan sisään tai suoraan ikkunan alaisuuteen. Widgetin tyypistä riippuen siihen sisältyy esimerkiksi Label.

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

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

Tarkastellaan nyt muutamia 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-tiedostoja käsitellään 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 bittiarvo välitetään NewPhoto-funktiolle, joka palauttaa Img-rakenteen osoittimen, joka edustaa Tcl/Tk-kuvaa.
  4. Image-funktion läpi kulkiessaan Img-rakenteen osoitin muunnetaan merkkijonoksi, johon on lisätty -Image Option.
  5. Muuntaminen RAW-arvot sisältäväksi merkkijonoksi johtuu Label-widgetin luomisesta.

ICO- ja PNG-tiedostoja käsitellään vastaavalla 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-tiedostojen käsittelyprosessi on seuraava:

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

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

Tarkastellaanpa nyt "Optionin sisältävän merkkijonon" todellista olemusta:

1type rawOption string

Edellä mainittu Optionin 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- ja PNG-tiedostojen tapauksessa tarvitaan koodaus-/dekoodausprosessia. Muissa tapauksissa Data-funktion tuloksena olevaan merkkijonoon, joka on muunnettu tavumuotoon ilman erityistä muunnosta, lisätään vain -data Option.

Kuvien lataaminen valikko-widgetillä

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

Tarkastellaan ensin yksinkertaista valikko-widget-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 sovellusikkunan alkuperäisen koon.

Muokkaamme nyt GetOpenFile-funktioon määritettyä Command-funktiota luodaksemme ohjelman, joka lataa ja näyttää kuvia.

Ohjelman kirjoittamissuunnitelma on seuraava:

  1. Rajoita vain PNG- ja ICO-tiedostojen avaamiseen.
  2. Käsittele tiedostonvalintaikkunasta valitut tiedostot.
  3. Toteuta widget kuvien näyttämiseen.

Seuraavassa koodissa on otettu huomioon tämä suunnitelma:

 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 seuraavalla tavalla:

  1. strings-paketin merkkijonojen vertailufunktiolla tarkistetaan tiedoston laajennus.
  2. os-pakettia käytetään tiedoston avaamiseen, lukemiseen ja sulkemiseen.
  3. Luettu kuva näytetään Label-widgetillä.

Yhteenveto

Tässä artikkelissa käsittelimme Go-kielen Tcl/Tk-kirjaston käyttöä seuraavissa asioissa:

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

Käyttämällä Go-kieltä ja Tcl/Tk:ta yhdessä voi luoda yksinkertaisia mutta käytännöllisiä GUI-ohjelmia. Tämän pohjalta voit kokeilla monimutkaisempien GUI-sovellusten kehittämistä.