GoSuda

Vytvorenie prehliadača obrázkov s pridaným zoznamom súborov z Go do Tk

By Yunjin Lee
views ...

V predchádzajúcom príspevku sme si stručne priblížili knižnicu CGo-Free Tcl/Tk. V tejto časti sa pokúsime vytvoriť prehliadač obrázkov aplikovaním príkladu z minulej časti.

Plán prehliadača obrázkov

  1. Predchádzajúci zobrazovač obrázkov nemal funkciu na mazanie obrázkov, takže čím viac obrázkov ste načítali, tým menšie bolo okno. Odstráňme nepoužívané štítky.
  2. Ak chcete uviesť viacero obrázkov v zozname zobrazovača obrázkov, vytvorme zoznam.
  3. Je lepšie načítať viacero obrázkov naraz.
  4. Implementujme aj odstránenie obrázkov, ktoré nebudete prezerať, zo zoznamu.
  5. Vytvorme funkciu na výber a zobrazenie konkrétneho obrázku spomedzi viacerých obrázkov.

Upravená knižnica Tcl/Tk 9.0

Existujúca knižnica má nedostatočnú implementáciu Listbox, čo sťažuje zobrazenie zoznamu obrázkov. Stiahnime si upravenú knižnicu. Ak nemáte nainštalované Git CLI, môžete si ju stiahnuť ako tarball alebo zip súbor.

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

Najprv sa pozrime na niektoré z pridaných funkcií.

Predtým, ako sa pozrieme na nové funkcie, sa stručne pozrime na štruktúru existujúcich funkcií prostredníctvom funkcie Destroy na riadku 1017 v tk.go.

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

Táto funkcia je implementovaná tak, že odovzdáva operáciu vo formáte Tcl skriptu funkcii evalErr. To znamená, že na implementáciu požadovanej funkcie stačí odovzdať príkaz vo formáte príslušného Tcl skriptu.

Napríklad implementujme metódu na pridanie položky do Listboxu. Najprv sa pozrime na príkaz insert spomedzi príkazov dostupných pre listbox v oficiálnom manuáli pre Tcl skriptovanie.

Príkaz insert

Na stránke s popisom príkazu insert je uvedené, že insert vkladá zoznam položiek na konkrétny index. Potom na implementáciu tohto príkazu

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ť kód ako tento.

Teraz, keď už poznáme približný princíp implementácie, vysvetlím 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 funkcia AddItem vkladá rôzne položky oddelené medzerami postupne od indexu 0. Položky item1-item3 budú mať postupne indexy 0, 1, 2. Spustite príklad a zistite, ako funguje odstránenie položiek.

Zoznam: Získanie vybratých položiek

Teraz skontrolujeme vybraté položky z Listboxu po ich získaní.

 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íska indexy všetkých aktuálne vybratých položiek v Listboxe. Metóda GetOne získa hodnotu položky zodpovedajúcej danému indexu. Výsledok je možné vidieť na konzole. Pre informáciu, podobná metóda Get prijíma počiatočný a koncový index a získa všetky hodnoty položiek v rámci rozsahu.

Ďalej zmeníme farbu Listboxu.

Najprv sa pozrime na 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 aplikácie farby

Ako bolo napísané v kóde vyššie, výška sa zväčšila. Tiež je vidieť, že farby boli správne aplikované. Tu je špecifikovaná možnosť, ktorá mení farbu len pre konkrétnu položku, takže je zrejmé, že len prvý riadok má inú farbu.

Okrem toho, hoci nie je veľký rozdiel, pomocou metódy Relief je možné zmeniť štýl okraja widgetu na flat, groove, raise, ridge, solid, sunken.

Príklad prehliadača obrázkov

Teraz vytvorme prehliadač obrázkov pomocou widgetu, ktorý sme sa naučili. 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)) // 다중 선택을 활성화하고 필터를 웁니다. (Aktivuje viacnásobný výber a zapne filtre.)
 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 názov témy a cestu.)
139    fileMenu := menubar.Menu()
140    extensions = make([]FileType,0,1)
141    extensions = append(extensions, FileType{ "Supported Images", []string {".png",".ico"}, "" } )
142    //필터에 png와 ico를 넣어 줍니다. (Pridajte png a ico do filtra.)
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, pre zjednodušenie implementácie, sú všetky widgety obrázkov vytvorené vopred pri ich načítaní, a duplicitné súbory sa nekontrolujú. Môžete vylepšiť vyššie spomenuté problémy, alebo môžete skúsiť zmeniť tému pomocou metódy DefaultTheme, ktorá je zakomentovaná. Odporúča sa precvičiť si vytvorenie nového programu, ktorý vylepší tieto aspekty.

Zhrnutie

V tomto článku sme sa dozvedeli, ako fungujú volania 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. Ako používať Listbox
  3. Zmena vlastností widgetu Listbox
  4. Vytvorenie prehliadača obrázkov

Týmto spôsobom sa pokúste modifikovať aj iné knižnice a vytvorte kompletný program s pridanými funkciami.