Implementering av en bildevisar med flerfilslisteboks i Go og Tcl/Tk 9.0

Forrige innlegg så på CGo-Free Tcl/Tk-biblioteket i korte trekk. I dette innlegget skal vi bygge en bildevisning ved å videreutvikle eksemplet fra sist.
Plan for bildevisningen
- Bildevisningen fra forrige gang hadde ingen funksjon for å slette bilder, så vinduet ble for lite etter hvert som flere bilder ble lastet inn. La oss slette etiketter som ikke lenger brukes.
- Hvis bildevisningen skal liste flere bilder, bør vi lage en liste.
- Det er lurt å kunne laste inn flere bilder samtidig.
- Vi implementerer også funksjonen for å fjerne bilder fra listen når de ikke skal vises.
- La oss lage en funksjon der man kan velge og vise ett bestemt bilde blant flere.
Endret Tcl/Tk 9.0-bibliotek
Det opprinnelige biblioteket har en mangelfull implementasjon av Listbox, og det er vanskelig å vise en bildeliste med det. La oss laste ned det endrede biblioteket. Hvis Git CLI ikke er installert, kan du i stedet laste ned tarball- eller zip-filen.
1git clone https://github.com/gg582/tk9.0
La oss først se på noen av de nye funksjonene.
Før vi ser på de nye funksjonene, skal vi først undersøke hvordan de eksisterende funksjonene er bygget opp. Vi tar utgangspunkt i funksjonen Destroy i tk.go på linje 1017.
1func Destroy(options ...Opt) {
2 evalErr(fmt.Sprintf("destroy %s", collect(options...)))
3}
Denne funksjonen er implementert ved å sende handlingen som et Tcl-skript til funksjonen evalErr. Det betyr at for å implementere en ønsket funksjon er det tilstrekkelig å sende kommandoen i form av det tilsvarende Tcl-skriptet.
Som et eksempel implementerer vi en metode som legger til elementer i en listboks. Først undersøker vi insert, en av kommandoene for listbox, i den offisielle manualen.
Av beskrivelsen av insert-kommandoen fremgår det at insert setter inn de oppførte elementene ved en bestemt indeks. For å implementere dette kan vi skrive for eksempel:
1func (l *ListboxWidget) AddItem(index int, items string) {
2 evalErr(fmt.Sprintf("%s insert %d %s", l.fpath, index, items))
3}
Nå som vi har forstått det grunnleggende prinsippet, skal vi forklare de ekstra funksjonene for Listbox.
Liste: innsetting/sletting
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 programmet over setter AddItem elementene som er atskilt med mellomrom inn fortløpende fra indeks 0. item1 til item3 får henholdsvis indeksene 0, 1 og 2. La oss kjøre eksemplet for å se hvordan sletting av elementer fungerer.
Liste: hente valgte elementer
Nå henter vi de valgte elementene fra Listbox og kontrollerer 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 indeksene til alle elementene som er valgt i Listbox. Metoden GetOne henter verdien til elementet med den gitte indeksen. Dette kan vi se av resultatet som skrives ut i konsollen. Merk at den tilsvarende metoden Get henter verdiene til alle elementene innenfor et område, gitt ved start- og sluttindeks.
Deretter skal vi endre fargene på listboksen.
La oss 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 koden over viser, har høyden økt slik vi angav. Vi ser også at fargene er brukt korrekt. Her er det angitt et alternativ som gir bestemte elementer en annen farge, så det er bare den første linjen som har en annen farge.
Relief-metoden kan dessuten brukes til å endre stilen på kanten av en widget til flat, groove, raise, ridge, solid eller sunken. Forskjellen er ikke stor, men det er en mulighet.
Eksempel på bildevisning
La oss nå bruke widgetene vi har lært om til å lage en bildevisning. Eksempelprogrammet ser slik ut:
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 eksemplet lages alle bildewidgetene på forhånd når bildene lastes inn, for å gjøre implementasjonen enkel, og det blir ikke kontrollert for duplikate filer. De nevnte problemene kan forbedres, og du kan også prøve å endre temaet ved hjelp av DefaultTheme-metoden som er satt som kommentar. Vi oppfordrer deg til å øve ved å lage et forbedret program med disse endringene.
Oppsummering
I denne artikkelen så vi på hvordan kommandokall i Tcl/Tk-biblioteket for Go fungerer, og vi laget en bildevisning med listboks.
- Metoden for kommandokall i Tcl/Tk-biblioteket
- Bruk av listboks
- Endring av egenskaper for listbox-widgeten
- Utvikling av bildevisning
Vi oppfordrer deg til å prøve den samme tilnærmingen på andre biblioteker og å lage et komplett program med de nye funksjonene.