GoSuda

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

By Yunjin Lee views ...

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

  1. 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.
  2. Hvis billedvisningsprogrammet skal vise flere billeder i en liste, lad os oprette en liste.
  3. Det er en god idé at indlæse flere billeder på én gang.
  4. Lad os også implementere funktionen til at fjerne billeder, som man ikke vil se, fra listen.
  5. 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.

insert-kommando

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}

Resultat af farveanvendelse

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

Billedfremviserens kørselsresultat

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.

  1. Metode til kommandokald i Tcl/Tk-biblioteket
  2. Brug af listbox
  3. Ændring af egenskaber for listbox-widgetten
  4. 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.