GoSuda

Go ve Tcl/Tk 9.0 ile Çoklu Dosya Liste Kutusu Tabanlı Görüntü Görüntüleyici Uygulaması

By Yunjin Lee views ...

Önceki yazıda CGo-Free Tcl/Tk kütüphanesini kısaca inceledik. Bu sefer önceki örneği geliştirerek bir görüntü görüntüleyici oluşturmaya çalışacağız.

Görüntü Görüntüleyici Planı

  1. Önceki görüntü gösterici, görüntü silme özelliğine sahip olmadığı için birden fazla görüntü yüklendikçe pencere boyutu yetersiz kalıyordu. Kullanılmayan etiketleri silelim.
  2. Görüntü göstericide birden fazla görüntüyü listeleyeceksek bir liste oluşturalım.
  3. Birden fazla görüntüyü tek seferde yüklemek iyi olacaktır.
  4. Görmek istemediğimiz görüntüleri listeden çıkarma işlevini de uygulayalım.
  5. Çoklu görüntüler arasından belirli bir görüntüyü seçip görüntüleme özelliği oluşturalım.

Değiştirilmiş Tcl/Tk 9.0 Kütüphanesi

Mevcut kütüphanede Listbox uygulaması yetersiz olduğu için görüntü listesini göstermek zordur. Değiştirilmiş kütüphaneyi indirelim. Git CLI yüklü değilse tarball veya zip dosyası olarak da indirebilirsiniz.

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

Öncelikle eklenen özelliklerden bazılarına bakalım.

Yeni fonksiyonları incelemeden önce, mevcut fonksiyonların nasıl yapılandırıldığını tk.go dosyasının 1017. satırındaki Destroy fonksiyonu üzerinden kısaca inceleyelim.

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

Bu fonksiyon, işlemi Tcl betik biçiminde evalErr fonksiyonunaileterek uygulanmıştır. Bu, istenen işlevi gerçekleştirmek için yalnızca ilgili Tcl betik biçiminde komutu iletmenin yeterli olduğu anlamına gelir.

Örnek olarak, bir liste kutusuna öğe ekleyen bir metot uygulayalım. Öncelikle Tcl betiği için resmi kılavuzda listbox'ta kullanılabilen komutlardan insert'e bakalım.

insert komutu

insert komutunun açıklama sayfasına bakıldığında, insert belirtilen dizine sıralanmış öğeleri ekler. Bunu uygulamak için

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

gibi bir kod yazabiliriz.

Artık genel uygulama mantığını anladığımıza göre, Listbox için eklenen ek işlevleri açıklayalım.

Liste: Ekleme/Silme

 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

Yukarıdaki programda AddItem, boşlukla ayrılmış farklı öğeleri 0 indeksinden başlayarak sırayla ekler. item1-item3 öğeleri sırasıyla 0, 1, 2 indekslerini alır. Öğe silmenin nasıl çalıştığını örneği çalıştırarak görelim.

Liste: Seçili Öğeleri Alma

Şimdi Listbox'ta seçilen öğeleri alıp kontrol edelim.

 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 metodu, mevcut Listbox'ta seçili tüm öğelerin indekslerini alır. GetOne metodu ise ilgili indeksteki öğenin değerini alır. Bu, konsola yazdırılan sonuçtan anlaşılabilir. Not olarak, benzer bir metot olan Get, başlangıç ve bitiş indekslerini alarak aralıktaki tüm öğelerin değerlerini alır.

Sırada liste kutusunun renklerini değiştirmek var.

Öncelikle aşağıdaki örneğe bakalım.

 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}

색상 적용 결과

Kodda yazıldığı gibi yükseklik arttı. Ayrıca renklerin doğru uygulandığını görebilirsiniz. Burada yalnızca belirli bir öğeye farklı renk uygulayan bir seçenek belirtildiği için yalnızca ilk satırın renginin farklı uygulandığı görülmektedir.

Ayrıca büyük bir fark yaratmasa da, Relief metodu ile flat, groove, raise, ridge, solid ve sunken seçenekleri arasından widget kenarlığının stilini değiştirebilirsiniz.

Görüntü Görüntüleyici Örneği

Şimdi öğrendiğimiz widget'ları kullanarak bir görüntü görüntüleyici oluşturalım. Örnek program aşağıdaki gibidir.

  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

이미지 뷰어 실행 결과

Bu örnekte uygulamayı basitleştirmek için tüm görüntü widget'ları yüklenirken önceden oluşturulmakta ve yinelenen dosyalar kontrol edilmemektedir. Yukarıda bahsedilen sorunlar iyileştirilebilir; ayrıca yorum satırına alınmış DefaultTheme metodunu kullanarak temayı değiştirmeyi de deneyebilirsiniz. Bu noktaları geliştirdiğiniz yeni bir program yazarak pratik yapmanızı öneririz.

Sonuç

Bu yazıda, Go dilindeki Tcl/Tk kütüphanesinin komut çağrılarının nasıl çalıştığını inceledik ve liste kutusu eklenmiş bir görüntü görüntüleyici oluşturduk.

  1. Tcl/Tk kütüphanesinde komut çağırma yöntemi
  2. Liste kutusu kullanım yöntemi
  3. Liste kutusu widget'ının özelliklerinin değiştirilmesi
  4. Görüntü görüntüleyici geliştirme

Bu yöntemle diğer kütüphanelerin değiştirilmesine de girişebilir ve eklediğiniz özelliklerle tamamlanmış bir program yazmayı deneyebilirsiniz.