GoSuda

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

By Yunjin Lee views ...

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

  1. 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.
  2. Om bildvisaren ska lista flera bilder, låt oss skapa en lista.
  3. Det är bra att kunna läsa in flera bilder på en gång.
  4. Vi implementerar även funktionen att ta bort bilder som inte ska visas från listan.
  5. 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.

insert-kommando

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}

Resultat med färgtillämpning

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

Körresultat för bildvisaren

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.

  1. Metod för kommandoanrop i Tcl/Tk-biblioteket
  2. Användning av listbox
  3. Ändring av egenskaper för listbox-widgeten
  4. 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.