GoSuda

Tworzenie przeglądarki obrazów z listą plików w Tk w Go

By Yunjin Lee
views ...

W poprzednim wpisie przedstawiono krótkie omówienie biblioteki Tcl/Tk CGo-Free. W tej części, bazując na poprzednim przykładzie, stworzymy przeglądarkę obrazów.

Plan przeglądarki obrazów

  1. Poprzednia przeglądarka obrazów nie posiadała funkcji usuwania obrazów, co powodowało, że okno stawało się zbyt małe przy ładowaniu wielu obrazów. Usuńmy nieużywane etykiety.
  2. Jeśli zamierzamy wyświetlać listę wielu obrazów w przeglądarce obrazów, stwórzmy listę.
  3. Zaleca się ładowanie wielu obrazów jednocześnie.
  4. Należy również zaimplementować funkcję usuwania obrazów, których nie zamierzamy oglądać, z listy.
  5. Stwórzmy funkcję umożliwiającą wybór i przeglądanie konkretnego obrazu spośród wielu.

Zmodyfikowana biblioteka Tcl/Tk 9.0

Istniejąca biblioteka ma niedoskonałą implementację Listbox, co utrudnia wyświetlanie listy obrazów. Pobierzmy zmodyfikowaną bibliotekę. Jeśli Git CLI nie jest zainstalowany, można pobrać plik tarball lub zip.

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

Najpierw przyjrzyjmy się kilku nowo dodanym funkcjom.

Zanim przejdziemy do nowych funkcji, przyjrzyjmy się, jak zbudowane są istniejące funkcje, analizując funkcję Destroy w linii 1017 pliku tk.go.

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

Ta funkcja jest zaimplementowana poprzez przekazanie operacji w formacie skryptu Tcl do funkcji evalErr. Oznacza to, że aby zaimplementować pożądaną funkcjonalność, wystarczy przekazać polecenie w formacie odpowiedniego skryptu Tcl.

Na przykład, zaimplementujmy metodę dodawania elementów do Listbox. Najpierw, w celu skryptowania Tcl, przyjrzyjmy się poleceniu insert wśród poleceń dostępnych dla listbox w oficjalnej instrukcji.

Polecenie insert

Strona opisu polecenia insert wskazuje, że insert wstawia wymienione elementy pod określony indeks. W takim razie, aby to zaimplementować, można napisać kod podobny do:

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

Teraz, gdy znamy ogólną zasadę implementacji, przejdźmy do wyjaśnienia dodatkowych funkcji dla Listbox.

Lista: Wstawianie/Usuwanie

 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

W powyższym programie funkcja AddItem wstawia rozdzielone spacjami elementy począwszy od indeksu 0. item1-item3 otrzymają kolejno indeksy 0, 1, 2. Uruchom przykład, aby zobaczyć, jak działa usuwanie elementów.

Lista: Pobieranie wybranych elementów

Teraz pobierzemy i sprawdzimy wybrane elementy z Listbox.

 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}

Metoda Selected pobiera indeksy wszystkich aktualnie wybranych elementów w Listbox. Metoda GetOne pobiera wartość elementu odpowiadającego danemu indeksowi. Wynik można zobaczyć w konsoli. Należy pamiętać, że podobna metoda Get pobiera indeksy początkowy i końcowy, a następnie pobiera wszystkie wartości elementów w danym zakresie.

Następnie zmienimy kolor Listbox.

Najpierw przyjrzyjmy się poniższemu przykładowi.

 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}

Wynik zastosowania kolorów

Jak napisano w powyższym kodzie, wysokość została zwiększona. Ponadto, kolory zostały dobrze zastosowane. Można zauważyć, że pierwsza linia ma inny kolor, ponieważ określono opcję zmiany koloru tylko dla konkretnego elementu.

Ponadto, chociaż różnica nie jest duża, metoda Relief może być użyta do zmiany stylu obramowania widżetu na flat, groove, raise, ridge, solid, sunken.

Przykład przeglądarki obrazów

Teraz użyjemy wcześniej poznanych widżetów do stworzenia przeglądarki obrazów. Przykładowy program przedstawia się następująco:

  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)) // Aktywuje wielokrotny wybór i włącza filtr.
 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    // Jeśli chcesz użyć motywu, określ jego nazwę i ścieżkę.
139    fileMenu := menubar.Menu()
140    extensions = make([]FileType,0,1)
141    extensions = append(extensions, FileType{ "Supported Images", []string {".png",".ico"}, "" } )
142    // Dodaje png i 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

Wynik uruchomienia przeglądarki obrazów

W tym przykładzie, dla uproszczenia implementacji, wszystkie widżety obrazów są tworzone z góry podczas ładowania, i nie jest sprawdzana duplikacja plików. Można poprawić wspomniane wcześniej problemy lub zmienić motyw za pomocą metody DefaultTheme, która jest zakomentowana. Zachęcamy do ćwiczenia, tworząc nowy program, który ulepsza te aspekty.

Podsumowanie

W tym artykule omówiono, w jaki sposób działają wywołania poleceń biblioteki Tcl/Tk w języku Go, oraz stworzono przeglądarkę obrazów z dodanym Listboxem.

  1. Metoda wywoływania poleceń biblioteki Tcl/Tk
  2. Sposób użycia Listbox
  3. Zmiana właściwości widżetu Listbox
  4. Tworzenie przeglądarki obrazów

Zachęcamy do spróbowania modyfikacji innych bibliotek w podobny sposób i tworzenia kompletnych programów z dodanymi funkcjami.