Implementering av en bildvisare med flerfilslistbox i Go och Tcl/Tk 9.0

I föregående inlägg tittade vi kort på det CGo-fria Tcl/Tk-biblioteket. I detta avsnitt ska vi tillämpa exemplet från förra gången för att skapa en bildvisare.
Plan för bildvisaren
- Bildvisaren från förra gången saknade funktion för att ta bort bilder, så fönstrets storlek räckte inte till när fler bilder lästes in. Låt oss ta bort etiketter som inte används.
- Om bildvisaren ska lista flera bilder, låt oss skapa en lista.
- Det är bra att kunna läsa in flera bilder på en gång.
- Vi implementerar även funktionen att ta bort bilder som inte ska visas från listan.
- Låt oss skapa en funktion för att välja och visa en specifik bild bland flera bilder.
Modifierat Tcl/Tk 9.0-bibliotek
Det befintliga biblioteket har bristfälligt stöd för Listbox, vilket gör det svårt att visa en bildlista. Låt oss ladda ner det modifierade biblioteket. Om Git CLI inte är installerat kan du också ladda ner det som tarball eller zip-fil.
1git clone https://github.com/gg582/tk9.0
Låt oss först titta på några av de tillagda funktionerna.
Innan vi undersöker de nya funktionerna, låt oss kort förstå strukturen hos de befintliga funktionerna genom Destroy-funktionen på rad 1017 i tk.go.
1func Destroy(options ...Opt) {
2 evalErr(fmt.Sprintf("destroy %s", collect(options...)))
3}
Denna funktion är implementerad genom att skicka en åtgärd i form av ett Tcl-skript till funktionen evalErr. Det innebär att för att implementera önskad funktion räcker det att skicka kommandot i formatet för motsvarande Tcl-skript.
Som ett exempel implementerar vi en metod som lägger till ett objekt i en listbox. Först tittar vi i den officiella manualen på insert, ett av de kommandon som kan användas för listbox.
På beskrivningssidan för insert framgår att insert infogar de angivna objekten vid ett specifikt index. För att implementera detta kan vi därför skriva kod såsom
1func (l *ListboxWidget) AddItem(index int, items string) {
2 evalErr(fmt.Sprintf("%s insert %d %s", l.fpath, index, items))
3}
Nu när vi förstått den grundläggande implementeringsprincipen, förklarar vi först ytterligare funktioner för Listbox.
Lista: infogning/borttagning
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 ovan lägger AddItem in de olika objekten, separerade med mellanslag, i följd från index 0. item1 till item3 får därmed indexen 0, 1 respektive 2. Vi kör exemplet för att se hur borttagning av objekt fungerar.
Lista: hämta valda objekt
Nu hämtar vi de objekt som valts i Listbox och kontrollerar 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 hämtar indexen för alla objekt som är valda i den aktuella Listbox. Metoden GetOne hämtar värdet för objektet på det angivna indexet. Vi kan se resultatet i konsolutskriften. Observera att den liknande metoden Get tar emot start- och slutindex och hämtar värdena för alla objekt inom intervallet.
Därefter ska vi ändra färgerna på listboxen.
Låt oss först titta på exemplet nedan.
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 ovan anger har höjden ökat. Dessutom ser vi att färgerna har tillämpats korrekt. Eftersom en inställning för färg endast för specifika objekt har angetts, ser vi att endast första raden har en annan färg.
Även om skillnaden inte är stor, kan metoden Relief användas för att ändra kantlinjens stil för widgeten bland flat, groove, raise, ridge, solid och sunken.
Exempel på bildvisare
Låt oss nu använda de widgetar vi just lärt oss för att skapa en bildvisare. Exempelprogrammet ser ut så här.
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 detta exempel skapas alla bildwidgetar i förväg när bilderna läses in, för att göra implementeringen enkel, och dubbletter av filer kontrolleras inte. Du kan förbättra de problem som nämnts ovan, och du kan också prova att ändra temat genom att använda metoden DefaultTheme som är utkommenterad. Prova gärna att skriva ett nytt program med dessa förbättringar för att öva.
Sammanfattning
I detta inlägg har vi undersökt hur kommandoanrop i Tcl/Tk-biblioteket för Go fungerar, och skapat en bildvisare med listbox.
- Metod för kommandoanrop i Tcl/Tk-biblioteket
- Användning av listbox
- Ändring av egenskaper för listbox-widgeten
- Skapande av en bildvisare
Vi uppmuntrar dig att på liknande sätt utmana dig själv med att modifiera andra bibliotek och skriva ett färdigt program med de funktioner du lagt till.