GoSuda

Implementácia prehliadača obrázkov s viacsúborovým zoznamovým poľom založeného na Go a Tcl/Tk 9.0

By Yunjin Lee views ...

V minulom príspevku sme stručne predstavili knižnicu CGo-Free Tcl/Tk. V tomto článku vytvoríme prehliadač obrázkov, pričom vychádzame z príkladu z minulého príspevku.

Plán prehliadača obrázkov

  1. Zobrazovač obrázkov z minulého príspevku nemal funkciu odstránenia obrázkov, preto sa s rastúcim počtom načítaných obrázkov okno stávalo nedostatočne veľkým. Odstráňme nepoužívané štítky.
  2. Ak chceme zobrazovač obrázkov použiť na zoznam viacerých obrázkov, vytvorme List.
  3. Je vhodné načítať viacero obrázkov naraz.
  4. Implementujme aj odstránenie obrázkov, ktoré si nechceme pozrieť, zo zoznamu.
  5. Vytvorme funkciu, ktorá umožní vybrať a zobraziť konkrétny obrázok z viacerých obrázkov.

Upravená knižnica Tcl/Tk 9.0

Pôvodná knižnica má nedostatočnú implementáciu Listbox, a preto je ťažké zobraziť zoznam obrázkov. Stiahnime upravenú knižnicu. Ak nemáte nainštalované Git CLI, môžete knižnicu stiahnuť aj ako archív tarball alebo zip.

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

Najprv si preskúmajme niektoré z pridaných funkcií.

Skôr než preskúmame nové funkcie, pozrime sa stručne na štruktúru existujúcich funkcií na základe funkcie Destroy v riadku 1017 súboru tk.go.

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

Táto funkcia je implementovaná tak, že odovzdáva svoju činnosť funkcii evalErr vo forme skriptu Tcl. Inými slovami, na implementáciu požadovanej funkcie stačí odovzdať príkaz vo formáte zodpovedajúceho skriptu Tcl.

Ako príklad implementujme metódu na pridávanie položiek do Listboxu. Najprv si pre skriptovanie v Tcl v oficiálnej príručke pozrime príkaz insert, ktorý je dostupný pre listbox.

Príkaz insert

Z popisu príkazu insert vyplýva, že insert vkladá zadané položky na určený index. Na jeho implementáciu teda

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

Môžeme napísať napríklad takýto kód.

Keď už poznáme približný princíp implementácie, vysvetlíme najprv ďalšie funkcie pre Listbox.

Zoznam: vkladanie/odstraňovanie

 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

V uvedenom programe AddItem vkladá od indexu 0 po poradí rôzne položky oddelené medzerníkom. Položky item1 až item3 tak získajú po rade indexy 0, 1 a 2. Ako funguje odstraňovanie položiek, zistíme spustením príkladu.

Zoznam: získanie vybraných položiek

Teraz získame vybrané položky z Listboxu a overíme ich.

 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}

Metóda Selected získava indexy všetkých položiek vybraných v aktuálnom Listboxe. Metóda GetOne získava hodnotu položky zodpovedajúcej danému indexu. Túto skutočnosť je možné overiť na výstupe vypísanom do konzoly. Pre úplnosť, podobná metóda Get prijíma počiatočný a koncový index a získava hodnoty všetkých položiek v rozsahu.

Ďalej zmeníme farby Listboxu.

Najprv si pozrime nasledujúci príklad.

 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}

Výsledok použitia farieb

Ako je vidieť v uvedenom kóde, výška sa zväčšila. Okrem toho je vidieť, že farby sa správne uplatnili. Keďže tu je nastavená možnosť, ktorá mení farbu iba pre konkrétne položky, vidíme, že iba prvý riadok má inú farbu.

Hoci rozdiel nie je veľký, metódou Relief môžeme zmeniť štýl okraja widgetu medzi hodnotami flat, groove, raise, ridge, solid a sunken.

Príklad prehliadača obrázkov

Teraz vytvoríme prehliadač obrázkov pomocou widgetov, ktorých použitie sme si práve ukázali. Príklad programu je nasledujúci.

  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)) //Povoľuje viacnásobný výber a zapína filter.
 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    //Ak chcete použiť tému, zadajte jej názov a cestu.
139    fileMenu := menubar.Menu()
140    extensions = make([]FileType,0,1)
141    extensions = append(extensions, FileType{ "Supported Images", []string {".png",".ico"}, "" } )
142    //Do filtra pridáme png a 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

Výsledok spustenia prehliadača obrázkov

V tomto príklade, aby bola implementácia jednoduchšia, vytvárame všetky widgety obrázkov už pri ich načítaní a neoverujeme duplicitné súbory. Spomenuté problémy môžete tiež odstrániť a tému môžete zmeniť pomocou metódy DefaultTheme, ktorej časť je zakomentovaná. Odporúčame vám precvičiť sa tak, že vytvoríte nový program, ktorý tieto časti vylepší.

Zhrnutie

V tomto článku sme sa oboznámili s tým, ako funguje volanie príkazov knižnice Tcl/Tk v jazyku Go, a vytvorili sme prehliadač obrázkov s pridaným Listboxom.

  1. Spôsob volania príkazov knižnice Tcl/Tk
  2. Spôsob použitia Listboxu
  3. Zmena atribútov widgetu Listbox
  4. Vytvorenie prehliadača obrázkov

Týmto spôsobom sa pokúste upraviť aj iné knižnice a na základe pridaných funkcií napíšte dokončený program.