GoSuda

Implementatie van een op meerdere bestanden gebaseerde afbeeldingsviewer met een listbox in Go en Tcl/Tk 9.0

By Yunjin Lee views ...

In het vorige bericht hebben we kort gekeken naar de CGo-vrije Tcl/Tk-bibliotheek. In dit bericht gaan we het vorige voorbeeld uitbreiden om een beeldviewer te maken.

Plan voor de beeldviewer

  1. De beeldweergave van de vorige keer had geen functie om afbeeldingen te verwijderen, dus naarmate er meer afbeeldingen werden geladen, was het venster te klein. Laten we labels die niet meer worden gebruikt, verwijderen.
  2. Als we de geladen afbeeldingen in de beeldweergave als lijst willen tonen, laten we dan een lijst maken.
  3. Het is handig om meerdere afbeeldingen tegelijk te kunnen laden.
  4. Laten we ook de functie implementeren om afbeeldingen die we niet meer willen bekijken uit de lijst te verwijderen.
  5. Laten we een functie maken waarmee we een specifieke afbeelding uit meerdere afbeeldingen kunnen selecteren en bekijken.

Gewijzigde Tcl/Tk 9.0-bibliotheek

In de oorspronkelijke bibliotheek is de implementatie van Listbox onvoldoende, waardoor het moeilijk is om een lijst met afbeeldingen weer te geven. Laten we de gewijzigde bibliotheek downloaden. Als Git CLI niet is geïnstalleerd, kun je ook het tarball- of zip-bestand downloaden.

1git clone https://github.com/gg582/tk9.0

Laten we eerst enkele van de toegevoegde functies bekijken.

Voordat we naar de nieuwe functies kijken, laten we eerst aan de hand van de functie Destroy in regel 1017 van tk.go kort de structuur van de bestaande functies doorgronden.

1func Destroy(options ...Opt) {
2	evalErr(fmt.Sprintf("destroy %s", collect(options...)))
3}

Deze functie is geïmplementeerd door de actie als Tcl-script door te geven aan de functie evalErr. Dat betekent dat we, om de gewenste functionaliteit te implementeren, alleen het overeenkomstige commando in de vorm van een Tcl-script hoeven door te geven.

Als voorbeeld implementeren we een methode die items aan een listbox toevoegt. Laten we eerst in de officiële handleiding kijken naar insert, een van de commando's die voor listbox beschikbaar zijn, voor het Tcl-scripting.

insert-commando

Op de beschrijvingspagina van het insert-commando zien we dat insert de opgegeven items invoegt op een bepaalde index. Om dit te implementeren kunnen we dan code schrijven zoals:

1func (l *ListboxWidget) AddItem(index int, items string) {
2	evalErr(fmt.Sprintf("%s insert %d %s", l.fpath, index, items))
3}

Nu we het globale implementatieprincipe begrijpen, lichten we eerst de extra functionaliteit voor Listbox toe.

Lijst: invoegen/verwijderen

 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

In het bovenstaande programma voegt AddItem de items, gescheiden door spaties, in vanaf index 0. item1 tot en met item3 krijgen respectievelijk de indexen 0, 1 en 2. Laten we het voorbeeld uitvoeren om te zien hoe het verwijderen van items werkt.

Lijst: geselecteerde items ophalen

Laten we nu de geselecteerde items uit de Listbox ophalen en controleren.

 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}

De methode Selected haalt de indexen op van alle items die momenteel in de Listbox zijn geselecteerd. De methode GetOne haalt de waarde op van het item met de overeenkomstige index. Dit is te zien aan de resultaten die in de console worden afgedrukt. Ter informatie: de vergelijkbare methode Get haalt, met een start- en eindindex, de waarden van alle items binnen dat bereik op.

Vervolgens gaan we de kleuren van de listbox aanpassen.

Laten we eerst het onderstaande voorbeeld bekijken.

 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}

Resultaat van kleurtoepassing

Zoals in de bovenstaande code is opgegeven, is de hoogte toegenomen. Ook is te zien dat de kleuren correct zijn toegepast. Doordat er een optie is ingesteld die alleen voor een specifiek item een andere kleur bepaalt, is te zien dat alleen de eerste regel een andere kleur heeft gekregen.

Hoewel het verschil niet groot is, kun je met de methode Relief de stijl van de widgetrand wijzigen in een van de volgende: flat, groove, raise, ridge, solid of sunken.

Voorbeeld van een beeldviewer

Laten we nu met de zojuist geleerde widgets een beeldviewer maken. Het voorbeeldprogramma ziet er als volgt uit.

  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

Resultaat van de beeldviewer

Om de implementatie eenvoudig te houden, worden in dit voorbeeld alle afbeeldingswidgets vooraf aangemaakt wanneer de afbeeldingen worden geladen, en wordt er niet gecontroleerd op dubbele bestanden. Je kunt de eerder genoemde problemen verbeteren, en ook het thema wijzigen door de als commentaar gemarkeerde DefaultTheme-methode te gebruiken. Oefen door een programma te maken waarin deze onderdelen zijn verbeterd.

Samenvatting

In dit artikel hebben we bekeken hoe de commando-aanroepen van de Tcl/Tk-bibliotheek voor Go werken, en hebben we een beeldviewer gemaakt met een toegevoegde listbox.

  1. Methode voor het aanroepen van commando's in de Tcl/Tk-bibliotheek
  2. Gebruik van de listbox
  3. Wijzigen van eigenschappen van de listbox-widget
  4. Het maken van een beeldviewer

Probeer op dezelfde manier ook andere bibliotheken aan te passen, en schrijf een voltooid programma met de toegevoegde functionaliteit.