GUI:n luominen Tk:lla Go-kielellä
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}
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}
Tässä kirjastossa SVG:n käsittely tapahtuu seuraavasti:
- SVG-tiedoston sisältö luetaan merkkijonoksi (tai sisällytetään suoraan kuten yllä olevassa esimerkissä).
- Tämä sisältö välitetään Data-funktiolle, joka muuntaa sen merkkijonoksi, joka sisältää Options (-data Option).
- Muunnettu bytedata välitetään NewPhoto-funktiolle, joka palauttaa Img-rakenteen osoittimen, joka edustaa Tcl/Tk-kuvaa.
- Image-funktion kautta Img-rakenteen osoitin muunnetaan merkkijonoksi, johon on lisätty -Image Option.
- 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}
PNG-tiedoston käsittelyprosessi on seuraava:
- Upotettu gopher.png muunnetaan merkkijonotyypiksi, joka sisältää Options.
- NewPhoto-funktion kautta se muunnetaan *Img-tyypiksi.
- 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}
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:
- Rajoita vain PNG- ja ICO-tiedostojen avaaminen
- Käsittele tiedostovalintaikkunasta valittu tiedosto
- 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}
Yllä oleva koodi toimii seuraavasti:
- Strings-paketin merkkijonovertailufunktiolla tarkistetaan tiedoston laajennus.
- Os-pakettia käytetään tiedoston avaamiseen, lukemiseen ja sulkemiseen.
- Luettu kuva näytetään Label-widgetinä.
Yhteenveto
Tässä artikkelissa käsiteltiin seuraavia asioita Go-kielen Tcl/Tk-kirjaston avulla:
- GUI-sovelluksen perusrakenne
- Erilaisten kuvamuotojen, kuten SVG, PNG ja ICO, käsittely
- Widgettien pakkaus ja asettelun hallinta
- Kuvadatan käsittelyrakenne
- 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.