GoSuda

Go- ja Tcl/Tk 9.0 -pohjainen monitiedostoinen listaruutuun perustuva kuvankatselin

By Yunjin Lee views ...

Edellisessä kirjoituksessa tarkastelimme lyhyesti CGo-Free Tcl/Tk -kirjastoa. Tällä kertaa sovellamme edellisen esimerkin pohjalta ja rakennamme kuvankatselimen.

Kuvankatselimen suunnitelma

  1. Edellisen kerran kuvan näyttäjässä ei ollut toimintoa kuvien poistamiseen, joten kun ladattiin useita kuvia, ikkuna ei riittänyt niille. Poistetaan käyttämättömät tarrat.
  2. Jos kuvat listataan kuvan näyttäjässä, luodaan lista niille.
  3. On parempi ladata useita kuvia kerralla.
  4. Toteutetaan myös toiminto, jolla kuvia, joita ei haluta katsoa, voidaan poistaa listalta.
  5. Luodaan toiminto, jolla voidaan valita ja tarkastella tiettyä kuvaa useiden kuvien joukosta.

Muokattu Tcl/Tk 9.0 -kirjasto

Alkuperäisessä kirjastossa Listbox-toteutus on puutteellinen, joten kuvalistan näyttäminen on vaikeaa. Ladataan muokattu kirjasto. Jos Git CLI ei ole asennettuna, voit ladata sen myös tarball- tai zip-tiedostona.

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

Tarkastellaan ensin joitakin lisättyjä toimintoja.

Ennen uusien funktioiden tarkastelua katsotaan, miten vanhat funktiot on toteutettu. Tutkitaan rakennetta lyhyesti tk.go-tiedoston rivin 1017 Destroy-funktion avulla.

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

Tämä funktio on toteutettu siten, että toiminto välitetään evalErr-funktiolle Tcl-skriptin muodossa. Toisin sanoen halutun toiminnon toteuttamiseksi riittää, että komento välitetään vastaavassa Tcl-skriptin muodossa.

Esimerkkinä toteutetaan menetelmä, jolla kohde lisätään listboxiin. Tutkitaan ensin Tcl-skriptausta varten virallisen käsikirjan listbox-komennoista insert.

insert-komento

Insert-komennon kuvauksen mukaan insert lisää annetut kohteet määritettyyn indeksiin. Tämän toteuttamiseksi voidaan kirjoittaa esimerkiksi seuraava koodi:

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

Kun perusperiaate on selvä, selitetään Listboxin lisätoiminnot.

Lista: lisäys ja poisto

 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

Edellisessä ohjelmassa AddItem lisää välilyönnillä erotetut eri kohteet indeksistä 0 alkaen järjestyksessä. item1-item3 saavat indeksit 0, 1 ja 2. Suoritetaan esimerkki ja katsotaan, miten kohteiden poisto toimii.

Lista: valittujen kohteiden hakeminen

Haetaan seuraavaksi Listboxista valitut kohteet ja tarkistetaan ne.

 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}

Selected-menetelmä hakee indeksit kaikista Listboxissa tällä hetkellä valituista kohteista. GetOne-menetelmä hakee kyseisen indeksin kohteen arvon. Tulos näkyy konsolissa. Samankaltainen Get-menetelmä hakee alku- ja loppuindeksin välillä olevien kohteiden arvot kaikki kerralla.

Seuraavaksi muutetaan listboxin väriä.

Tarkastellaan ensin seuraavaa esimerkkiä.

 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}

색상 적용 결과

Kuten koodissa on määritelty, korkeus on kasvanut. Lisäksi värit on sovellettu oikein. Koska erityinen asetus koskee vain tiettyä kohdetta, vain ensimmäinen rivi on eri värinen.

Relief-menetelmällä voidaan muuttaa widgetin reunuksen tyyliä: flat, groove, raise, ridge, solid tai sunken. Muutos ei ole kovin suuri.

Kuvankatselimen esimerkki

Rakennetaan nyt kuvankatselin edellä opittujen widgetien avulla. Esimerkkiohjelma on seuraava.

  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

이미지 뷰어 실행 결과

Tässä esimerkissä kaikki kuvawidgetit luodaan etukäteen, kun kuvat ladataan, jotta toteutus pysyy yksinkertaisena, eikä päällekkäisiä tiedostoja tarkisteta. Edellä mainittuja ongelmia voi parantaa, ja kommentoitua DefaultTheme-menetelmää käyttämällä voi myös kokeilla teeman vaihtamista. Harjoittele tekemällä ohjelmasta parannettu versio, jossa nämä asiat on huomioitu.

Yhteenveto

Tässä kirjoituksessa tarkastelimme, miten Go-kielen Tcl/Tk-kirjaston komentokutsut toimivat, ja rakensimme kuvankatselimen, johon lisättiin listbox.

  1. Tcl/Tk-kirjaston komentokutsujen toteutustapa
  2. Listboxin käyttö
  3. Listbox-widgetin ominaisuuksien muuttaminen
  4. Kuvankatselimen toteutus

Kokeile samaa lähestymistapaa muiden kirjastojen muokkaamiseen ja kirjoita lisättyjen toimintojen avulla valmis ohjelma.