GoSuda

Go és Tcl/Tk 9.0 használatával többfájlos, listadoboz alapú képnézegető megvalósítása

By Yunjin Lee views ...

Az előző bejegyzésben röviden áttekintettük a CGo-Free Tcl/Tk könyvtárat. Ezúttal az előző példát továbbfejlesztve egy képnézegetőt fogunk készíteni.

Képnézegető terv

  1. Az előző alkalom képmegjelenítőjében nem volt képtörlési funkció, ezért minél több képet töltöttünk be, annál kevesebb hely maradt az ablakban. Töröljük a nem használt címkéket.
  2. Ha a képmegjelenítőben több képet szeretnénk listába rendezni, hozzunk létre egy listát.
  3. Érdemes egyszerre több képet betölteni.
  4. Valósítsuk meg azt is, hogy a már nem szükséges képeket eltávolíthassuk a listából.
  5. Készítsünk olyan funkciót, amellyel a több kép közül egy adott képet kiválasztva megtekinthetjük.

Módosított Tcl/Tk 9.0 könyvtár

A meglévő könyvtárban a Listbox megvalósítása hiányos, ezért nehéz benne képlistát megjeleníteni. Töltsük le a módosított könyvtárat. Ha a Git CLI nincs telepítve, a tarball vagy zip fájlként történő letöltés is megfelelő.

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

Nézzük meg először a hozzáadott funkciók néhányat.

Mielőtt az új függvényeket megvizsgálnánk, a tk.go fájl 1017. sorában található Destroy függvény segítségével értsük meg a meglévő függvények felépítését.

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

Ez a függvény a műveletet Tcl szkript formában adja át az evalErr függvénynek. Ez azt jelenti, hogy a kívánt funkció megvalósításához elegendő a megfelelő Tcl szkript formátumában átadni a parancsot.

Példaként implementáljunk egy metódust, amely elemet ad hozzá a listaelemhez. Először a Tcl szkripthez nézzük meg a hivatalos kézikönyvben a listbox lehetséges parancsai közül az insert parancsot.

insert parancs

Az insert parancs leírásának oldalán látható, hogy az insert a megadott indexre beszúrja a felsorolt elemeket. Ennek megvalósításához a következő kód írható:

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

Most, hogy nagyjából megértettük a megvalósítás elvét, bemutatjuk a Listbox további funkcióit.

Lista: beszúrás/törlés

 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

A fenti programban az AddItem szóközzel elválasztott, különböző elemeket helyez el a 0-s indextől kezdve, sorrendben. Az item1-item3 elemek sorrendben a 0, 1, 2 indexet kapják. A példaprogram futtatásával megnézhetjük, hogyan működik az elemek törlése.

Lista: a kiválasztott elemek lekérdezése

Most a Listboxban kiválasztott elemeket kérjük le, majd ellenőrizzük őket.

 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}

A Selected metódus a Listboxban jelenleg kiválasztott összes elem indexét adja vissza. A GetOne metódus a megadott indexhez tartozó elem értékét adja vissza. Az eredmény a konzolra írt kimenetből látható. Érdemes megjegyezni, hogy a hasonló Get metódus kezdő és záró indexet kap, és az adott tartományon belüli összes elem értékét lekéri.

Következőként változtassuk meg a listaelem színeit.

Először nézzük meg az alábbi példát.

 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}

색상 적용 결과

A fenti kódban megadottak szerint a lista magassága megnőtt. Emellett a színek is helyesen alkalmazódtak. Itt olyan beállítás is szerepel, amely csak egy adott elem színét módosítja, ezért látható, hogy csak az első sor színe tér el.

Ezenkívül bár nagy különbség nincs, a Relief metódussal a widget szegélyének stílusát módosíthatjuk: flat, groove, raise, ridge, solid vagy sunken közül választhatunk.

Képnézegető példa

Most a korábban megismert widgetek segítségével készítsünk képnézegetőt. A példaprogram a következő:

  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

이미지 뷰어 실행 결과

Ebben a példában az egyszerűbb megvalósítás érdekében minden képwidgetet már a betöltéskor létrehozunk, és nem ellenőrizzük a duplikált fájlokat. A fentebb említett problémákat javíthatjuk, és a kommentelt DefaultTheme metódus segítségével is kipróbálhatunk egy másik témát. Gyakorlásként érdemes olyan programot írni, amely ezeket a részeket javítja.

Összefoglalás

Ebben a cikkben megnéztük, hogyan működnek a parancshívások a Go nyelvű Tcl/Tk könyvtárban, és elkészítettük a listaelemekkel bővített képnézegetőt.

  1. A Tcl/Tk könyvtár parancshívási módja
  2. A listbox használata
  3. A listbox widget tulajdonságainak módosítása
  4. Képnézegető készítése

Ugyanilyen módon érdemes más könyvtárak módosításaival is kísérletezni, és a hozzáadott funkciókkal teljes programot írni.