GoSuda

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

By Yunjin Lee
views ...

W poprzednim poście krótko omówiliśmy bibliotekę CGo-Free Tcl/Tk. Tym razem, wykorzystując przykład z poprzedniej lekcji, stworzymy przeglądarkę obrazów.

Plan przeglądarki obrazów

  1. Wyświetlacz obrazów z poprzedniej lekcji nie posiadał funkcji usuwania obrazów, co powodowało, że im więcej obrazów ładowano, tym bardziej brakowało miejsca na ekranie. Usuńmy nieużywane etykiety.
  2. Jeśli zamierzamy wyświetlać wiele obrazów w wyświetlaczu obrazów, stwórzmy listę.
  3. Zaleca się ładowanie wielu obrazów jednocześnie.
  4. Zaimplementujmy również funkcję usuwania obrazów, których nie będziemy 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 dodanym funkcjom.

Zanim przyjrzymy się nowym funkcjom, spróbujmy zrozumieć strukturę istniejących funkcji, 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 przekazywanie operacji w formacie skryptu Tcl do funkcji evalErr. Oznacza to, że aby zaimplementować pożądaną funkcję, wystarczy przekazać polecenie w odpowiednim formacie skryptu Tcl.

Na przykład, zaimplementujmy metodę dodawania elementów do Listbox. Najpierw, dla skryptowania Tcl, sprawdźmy polecenie insert wśród dostępnych poleceń dla listbox w oficjalnym podręczniku.

Polecenie insert

Strona opisu polecenia insert wskazuje, że insert wstawia wymienione elementy pod określonym indeksem. W takim razie, aby to zaimplementować, możemy 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ę do opisu 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 AddItem wstawia rozdzielone spacjami elementy, począwszy od indeksu 0. item1-item3 będą miały odpowiednio 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 z Listbox. Metoda GetOne pobiera wartość elementu odpowiadającego danemu indeksowi. Wynik jest widoczny w konsoli. Należy zauważyć, że podobna metoda Get pobiera indeksy początkowy i końcowy, aby pobrać wszystkie wartości elementów z danego zakresu.

Następnie zmienimy kolor Listbox.

Najpierw spójrzmy na poniższy przykład.

 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 koloru

Jak napisano w powyższym kodzie, wysokość została zwiększona. Widać również, że kolory zostały poprawnie zastosowane. Tutaj, ponieważ opcja zmiany koloru tylko dla konkretnego elementu jest określona, widać, że tylko pierwszy wiersz ma inny kolor.

Ponadto, choć nie ma dużej różnicy, metoda Relief pozwala zmienić styl obramowania widżetu na flat, groove, raise, ridge, solid lub sunken.

Przykład przeglądarki obrazów

Teraz, wykorzystując nowo poznane widżety, stworzymy przeglądarkę obrazów. Przykładowy program wygląda 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 filtry.
 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 działania przeglądarki obrazów

W tym przykładzie, aby uprościć implementację, wszystkie widżety obrazów są tworzone z góry podczas ładowania, i nie sprawdza się duplikatów plików. Można poprawić wspomniane wcześniej problemy, lub spróbować zmienić motyw, używając skomentowanej metody DefaultTheme. Zachęcam do ćwiczenia, tworząc nowy program, który poprawia te aspekty.

Podsumowanie

W tym artykule omówiliśmy, jak działają wywołania poleceń biblioteki Tcl/Tk w języku Go, i stworzyliśmy przeglądarkę obrazów z dodanym Listbox.

  1. Sposób 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

W ten sposób spróbuj modyfikować inne biblioteki i tworzyć kompletne programy z dodanymi funkcjami.