Implementering af en billedfremviser med flervalgsfilliste baseret på Go og Tcl/Tk 9.0

I det forrige indlæg gennemgik vi kort CGo-Free Tcl/Tk-biblioteket. I dette indlæg vil vi bygge videre på eksemplet fra sidst og lave en billedfremviser.
Plan for billedfremviseren
- Billedvisningsprogrammet fra sidste gang har ingen funktion til at slette billeder, så vinduets størrelse slog til, når der blev indlæst flere billeder. Lad os slette de labels, der ikke længere bruges.
- Hvis billedvisningsprogrammet skal vise flere billeder i en liste, lad os oprette en liste.
- Det er en god idé at indlæse flere billeder på én gang.
- Lad os også implementere funktionen til at fjerne billeder, som man ikke vil se, fra listen.
- Lad os lave en funktion, hvor man kan vælge og se et bestemt billede blandt flere billeder.
Modificeret Tcl/Tk 9.0-bibliotek
Det eksisterende bibliotek har en mangelfuld implementering af Listbox, hvilket gør det svært at vise en billedliste. Lad os downloade det modificerede bibliotek. Hvis Git CLI ikke er installeret, kan du også downloade det som tarball- eller zip-fil.
1git clone https://github.com/gg582/tk9.0
Lad os først se på nogle af de tilføjede funktioner.
Før vi kigger på de nye funktioner, lad os kort undersøge strukturen af de eksisterende funktioner ved hjælp af Destroy-funktionen i tk.go, linje 1017.
1func Destroy(options ...Opt) {
2 evalErr(fmt.Sprintf("destroy %s", collect(options...)))
3}
Denne funktion er implementeret ved at sende handlingen som et Tcl-script til funktionen evalErr. Det betyder, at man blot skal sende kommandoen i form af det tilsvarende Tcl-script for at implementere den ønskede funktion.
Som eksempel lad os implementere en metode, der tilføjer et element til en listbox. Lad os først se på insert-kommandoen blandt de kommandoer, der kan bruges med listbox, i den officielle manual.
På beskrivelsessiden for insert-kommandoen fremgår det, at insert indsætter de angivne elementer ved et bestemt indeks. For at implementere dette kan vi skrive en kode som:
1func (l *ListboxWidget) AddItem(index int, items string) {
2 evalErr(fmt.Sprintf("%s insert %d %s", l.fpath, index, items))
3}
Nu hvor vi har en grov forståelse af implementeringsprincippet, lad os gennemgå de yderligere funktioner til Listbox.
Liste: indsættelse/sletning
1package main
2import . "modernc.org/tk9.0"
3
4func main() {
5 length := 3
6 l := Listbox()
7 l.AddItem(0, "item1 item2 item3")
8 b1 := TButton(Txt("Delete Multiple Items, index (0-1)"), Command( func(){
9 if length >= 2 {
10 l.DeleteItems(0,1)
11 length-=2
12 }
13 }))
14 b2 := TButton(Txt("Delete One Item, index (0)"), Command( func () {
15 if length > 0 {
16 l.DeleteOne(0)
17 length-=1
18 }
19 }))
20 Pack(TExit(),l,b1,b2)
21 App.Wait()
22}
23
I ovenstående program indsætter AddItem de forskellige elementer, der er adskilt af mellemrum, fra indeks 0 og fremefter. item1-item3 får henholdsvis indeks 0, 1 og 2. Lad os køre eksemplet for at se, hvordan sletning af elementer fungerer.
Liste: hent valgte elementer
Lad os nu hente de elementer, der er valgt i Listbox, og kontrollere dem.
1package main
2
3import . "modernc.org/tk9.0"
4
5func main() {
6 l := Listbox()
7 l.SelectMode("multiple")
8 l.AddItem(0, "item1 item2 item3")
9 btn := TButton(Txt("Print Selected"), Command( func() {
10 sel := l.Selected()
11 for _, i := range sel {
12 println(l.GetOne(i))
13 }
14 }))
15
16 Pack(TExit(), l, btn)
17 App.Wait()
18}
Metoden Selected henter indekserne for alle elementer, der er valgt i den aktuelle Listbox. Metoden GetOne henter værdien for elementet med det tilsvarende indeks. Resultatet kan ses i konsolen. Bemærk, at den lignende metode Get tager et start- og slutindeks og henter værdierne for alle elementer inden for intervallet.
Dernæst lad os ændre farverne på listboxen.
Lad os først se på eksemplet nedenfor.
1package main
2
3import . "modernc.org/tk9.0"
4
5func main() {
6 l := Listbox()
7 l.Background("blue")
8 l.Foreground("yellow")
9 l.SelectBackground("black")
10 l.SelectForeground("white")
11 l.Height(20)
12 l.Width(6)
13 l.AddItem(0, "item1 item2 item3")
14 l.ItemForeground(0,"red")
15 l.ItemBackground(0,"green")
16 l.ItemSelectBackground(0,"white")
17 l.ItemSelectForeground(0,"black")
18 l.Relief("ridged")
19 Pack(TExit(),l)
20 App.Wait()
21}

Som angivet i koden ovenfor er højden øget. Det ses også, at farverne er blevet anvendt korrekt. Her er der angivet en indstilling, der kun ændrer farven for bestemte elementer, og derfor ses det, at kun den første linje har en anden farve.
Der er desuden en lille forskel, når man bruger metoden Relief, som gør det muligt at ændre stilen på widgetens kant til en af følgende: flat, groove, raise, ridge, solid eller sunken.
Eksempel på billedfremviser
Lad os nu bruge de widgets, vi lige har lært, til at lave en billedfremviser. Eksempelprogrammet er som følger.
1package main
2
3import (
4 "fmt"
5 "log"
6 "os"
7 "runtime"
8 "strings"
9 "path"
10
11 . "modernc.org/tk9.0"
12)
13
14var pbuttons []*TButtonWidget
15var extensions []FileType
16var pbutton *TButtonWidget = nil
17var listbox, listbox2 *ListboxWidget
18var cur *LabelWidget = nil
19var imagesLoaded []*LabelWidget
20func PhotoName(fileName string) string {
21 fileName = path.Base(fileName)
22 return fileName[:len(fileName)-len(path.Ext(fileName))]
23}
24
25func handleFileOpen() {
26 res := GetOpenFile(Multiple(true),Filetypes(extensions)) //다중 선택을 활성화하고 필터를 켭니다.
27 s := make([]string,0,1000)
28 for _, itm := range res {
29 if itm != "" {
30 tmp := strings.Split(itm," ")
31 s = append(s,tmp...)
32 }
33 }
34
35 for _, photo := range s {
36 formatCheck := strings.Split(photo, ".")
37 format := formatCheck[len(formatCheck)-1]
38
39 if (strings.Compare(format, "png") == 0) || (strings.Compare(format, "ico") == 0) {
40 picFile, err := os.Open(photo)
41 if err != nil {
42 log.Println("Error while opening photo, error is: ", err)
43 }
44
45 pictureRawData := make([]byte, 10000*10000)
46 picFile.Read(pictureRawData)
47
48 imageLabel := Label(Image(NewPhoto(Data(pictureRawData))))
49 imagesLoaded = append(imagesLoaded,imageLabel)
50 var deleteTestButton *TButtonWidget
51 deleteTestButton = TButton(
52 Txt("Unshow Image"),
53 Command(func() {
54 GridForget(imageLabel.Window)
55 GridForget(deleteTestButton.Window)
56 }))
57
58 pbuttons = append(pbuttons,deleteTestButton)
59
60 listbox.AddItem(len(imagesLoaded)-1,PhotoName(photo))
61 listbox2.AddItem(len(imagesLoaded)-1,PhotoName(photo))
62 picFile.Close()
63 }
64 }
65}
66
67func DeleteSelected () {
68 s:=listbox.Selected()
69 if len(s) == 0 {
70 return
71 }
72 for _, i := range s {
73 listbox.DeleteOne(i)
74 listbox2.DeleteOne(i)
75 if len(imagesLoaded)-1>i {
76 continue
77 }
78 if cur == imagesLoaded[i] {
79 pbutton = nil
80 cur = nil
81 }
82 Destroy(imagesLoaded[i])
83 Destroy(pbuttons[i])
84 imagesLoaded = append(imagesLoaded[:i],imagesLoaded[i+1:]...)
85 pbuttons = append(pbuttons[:i], pbuttons[i+1:]...)
86 }
87}
88
89func SelectImage() {
90 s:=listbox2.Selected()
91 if len(s) == 0 {
92 return
93 }
94
95 if len(imagesLoaded) -1 < s[0] {
96 return
97 }
98 if imagesLoaded[s[0]] == nil {
99 return
100 }
101 if cur != nil {
102 GridForget(cur.Window)
103 }
104 if pbutton != nil {
105 GridForget(pbutton.Window)
106 }
107
108 Grid(imagesLoaded[s[0]], Row(0), Column(2))
109 Grid(pbuttons[s[0]], Row(0), Column(3))
110 cur = imagesLoaded[s[0]]
111 pbutton = pbuttons[s[0]]
112}
113
114func SelectIndex(index int) {
115
116 if len(imagesLoaded) -1 <index {
117 return
118 }
119 if imagesLoaded[index] == nil {
120 return
121 }
122 if cur != nil {
123 GridForget(cur.Window)
124 }
125 if pbutton != nil {
126 GridForget(pbutton.Window)
127 }
128
129 Grid(imagesLoaded[index], Row(0), Column(2))
130 Grid(pbuttons[index], Row(0), Column(3))
131 cur = imagesLoaded[index]
132 pbutton = pbuttons[index]
133}
134
135func main() {
136 menubar := Menu()
137 //DefaultTheme("awdark","themes/awthemes-10.4.0")
138 //테마를 사용하고 싶을 때에는 테마 명과 경로를 지정해 줍니다.
139 fileMenu := menubar.Menu()
140 extensions = make([]FileType,0,1)
141 extensions = append(extensions, FileType{ "Supported Images", []string {".png",".ico"}, "" } )
142 //필터에 png와 ico를 넣어 줍니다.
143 fileMenu.AddCommand(Lbl("Open..."), Underline(0), Accelerator("Ctrl+O"), Command(func () {
144 handleFileOpen()
145 SelectIndex(len(imagesLoaded)-1)
146 } ))
147 Bind(App, "<Control-o>", Command(func() { fileMenu.Invoke(0) }))
148 fileMenu.AddCommand(Lbl("Exit"), Underline(1), Accelerator("Ctrl+Q"), ExitHandler())
149 Bind(App, "<Control-q>", Command(func() { fileMenu.Invoke(1) }))
150 menubar.AddCascade(Lbl("File"), Underline(0), Mnu(fileMenu))
151 imagesLoaded = make([]*LabelWidget, 0, 10000)
152 pbuttons = make([]*TButtonWidget,0,10000)
153 var scrollx, scroll, scroll2, scrollx2 *TScrollbarWidget
154 listbox = Listbox(Yscrollcommand(func(e *Event) { e.ScrollSet(scroll)}) , Xscrollcommand( func(e *Event) { e.ScrollSet(scrollx)}))
155 listbox2 = Listbox(Yscrollcommand(func(e *Event) { e.ScrollSet(scroll2)}), Xscrollcommand(func(e *Event) { e.ScrollSet(scrollx2)}))
156 listbox.SelectMode("multiple")
157 listbox2 = Listbox()
158 listbox.Background("white")
159 listbox.SelectBackground("blue")
160 listbox.SelectForeground("yellow")
161 listbox2.Background("grey")
162 listbox2.SelectBackground("green")
163 listbox2.SelectForeground("blue")
164 listbox2.SelectBackground("brown")
165 listbox.Height(5)
166 listbox.Width(4)
167 listbox2.Height(5)
168 listbox2.Width(4)
169 delBtn := Button(Txt("Delete"), Command(func () { DeleteSelected() }))
170 selBtn := Button(Txt("Select"), Command(func () { SelectImage() }))
171 scroll = TScrollbar(Command(func(e *Event) { e.Yview(listbox) }))
172 scrollx = TScrollbar(Orient("horizontal"),Command(func(e *Event) { e.Xview(listbox) }))
173 scroll2 = TScrollbar(Command(func(e *Event) { e.Yview(listbox2) }))
174 scrollx2 = TScrollbar(Orient("horizontal"),Command(func(e *Event) { e.Xview(listbox2) }))
175 Grid(listbox,Row(1),Column(0), Sticky("nes"))
176 Grid(scroll,Row(1),Column(1), Sticky("nes"))
177 Grid(scrollx,Row(2),Column(0), Sticky("nes"))
178 Grid(delBtn,Row(3),Column(0), Sticky("nes"))
179 Grid(listbox2,Row(1),Column(2), Sticky("nes"))
180 Grid(scroll2,Row(1),Column(3), Sticky("nes"))
181 Grid(scrollx2,Row(2),Column(2), Sticky("nes"))
182 Grid(selBtn,Row(3),Column(2), Sticky("nes"))
183 App.WmTitle(fmt.Sprintf("%s on %s", App.WmTitle(""), runtime.GOOS))
184 App.Configure(Mnu(menubar), Width("80c"), Height("60c")).Wait()
185}
186

I dette eksempel er alle billedwidgets oprettet på forhånd, når de indlæses, for at gøre implementeringen enkel, og der kontrolleres ikke for duplikerede filer. De ovenfor nævnte problemer kan forbedres, og den udkommenterede del, DefaultTheme-metoden, kan bruges til at ændre temaet. Vi opfordrer dig til at øve dig ved at lave et forbedret program med disse ændringer.
Opsummering
I denne artikel har vi set, hvordan kommandokald i Tcl/Tk-biblioteket til Go fungerer, og vi har lavet en billedfremviser med en listbox.
- Metode til kommandokald i Tcl/Tk-biblioteket
- Brug af listbox
- Ændring af egenskaber for listbox-widgetten
- Udvikling af en billedfremviser
Vi opfordrer dig til at prøve at ændre andre biblioteker på samme måde og at skrive et færdigt program med de funktioner, du har tilføjet.