Tkinter Programlama Dili

5. Tkinter Uygulamalarını Güzelleştirmek

Tkinter arayüz takımı konusunda en fazla şikayet edilen şey Tkinter ile yazılan uygulamaların “çirkin” görünmesidir. Bu bölümde bu sıkıntıyı aşmanın bazı yollarını göstermeye çalışacağız. Ayrıca bu bölümde, “güzellik” ile ilgili olduğunu düşündüğüm başka konulara da değineceğiz. Mesela bu bölümde, Tkinter uygulamaları içinde kullandığımız pencere araçlarına nasıl simge yerleştirebileceğimizi de inceleyeceğiz. Bu bölüm bir yandan da örnek uygulamalarla zenginleştirilmeye çalışılacaktır. Yani konuları anlatırken ufak program örnekleri üzerinden gitmeye çalışacağız...

5.1. Tkinter Programlarının Renk Şemasını Değiştirmek

Tkinter programlarını güzelleştirme yolunda ilk inceleyeceğimiz konu, yazdığımız uygulamarın renk şemasını değiştirmek olacaktır... Biz herhangi bir Tkinter uygulaması yazdığımız zaman, bu uygulamanın varsayılan bir renk şeması olacaktır. Hemen bir örnekle başlayalım:

#!/usr/bin/env python
#-*-coding:utf-8-*-

from Tkinter import *

pencere = Tk()

etiket = Label(text="Örnek Uygulama")
etiket.pack()

mainloop()

Buna benzer bütün uygulamalarımızda belli bir renk şeması varsayılan olarak pencerelerimize uygulanacaktır. Eğer biz müdahale etmez isek, oluşturacağımız bütün pencereler ve üzerindeki pencere araçları tek bir renkte görünecektir. Ama eğer istersek bu gidişe bir dur diyebiliriz. Bunu nasıl yapabileceğimizi daha önceki derslerimizde de kısmen görümüştük. Mesela şu seçenekler bize yardımcı oluyordu:

#!/usr/bin/env python
#-*-coding:utf-8-*-

from Tkinter import *

pencere = Tk()

etiket = Label(text="Örnek Uygulama",
               fg="white",
               bg="blue")

etiket.pack()

mainloop()

Burada, kullandığımız etiket (Label) adlı pencere aracını, “bg” ve “fg” seçenekleri yardımıyla farklı renklerde çizdik. Siz renk adları yerine, http://www.html-color-codes.info/ adresine başvurarak, renk kodlarını da kullanabileceğinizi biliyorsunuz...

Aslında benim kastettiğim “güzelleştirme” operasyonu sadece pencere araçlarının rengini değiştirmekle ilgili değil. Ben aynı zamanda, penceremizin rengini değiştirmekten de bahsediyorum. Lafla peynir gemisi yürümüyor, o yüzden isterseniz hemen bir örnek vererek ne demek istediğimizi biraz daha net olarak anlatmaya çalışayım. Şu örneğe bir bakın:

#!/usr/bin/env python
#-*-coding:utf-8-*-

from Tkinter import *

pencere = Tk()

pencere.tk_setPalette("#D0A9F5")

etiket = Label(text="Renk şeması değiştirilmiş bir örnek uygulama")
etiket.pack()

mainloop()

Gördüğünüz gibi, sadece tek bir pencere aracının rengini değil, aynı zamanda bütün bir pencerenin rengini değiştirdik. Bunu yapmamızı sağlayan şey de kodlar içinde kullandığımız, “pencere.tk_setPalette(“#D0A9F5”)” satırı oldu. Yani buradaki “tk_setPalette” adlı metodu kullanarak bütün pencerenin renk şemasını değiştirebildik. Bu “tk_setPalette()” komutu, Tk() sınıfının metotlarından yalnızca bir tanesidir. Bu sınıfın bize hangi metotları sunduğunu görmek için Python’un etkileşimli kabuğunda şu komutları verebilirsiniz:

from Tkinter import *
dir(Tk())

Bu komutu verdiğimizde şu devasa listeyi elde ediyoruz:

['_Misc__winfo_getint', '_Misc__winfo_parseitem', '__contains__',
'__doc__', '__getattr__', '__getitem__', '__init__', '__module__',
'__setitem__', '__str__', '_bind', '_configure', '_displayof',
'_getboolean', '_getdoubles', '_getints', '_grid_configure',
'_loadtk', '_nametowidget', '_noarg_', '_options', '_register',
'_report_exception', '_root', '_subst_format', '_subst_format_str',
'_substitute', '_tclCommands', '_tkloaded', '_w', 'adderrorinfo',
'after', 'after_cancel', 'after_idle', 'aspect', 'attributes',
'bbox', 'bell', 'bind', 'bind_all', 'bind_class', 'bindtags',
'call', 'cget', 'children', 'client', 'clipboard_append',
'clipboard_clear', 'clipboard_get', 'colormapwindows',
'colormodel', 'columnconfigure', 'command', 'config', 'configure',
'createcommand', 'createfilehandler', 'createtimerhandler',
'deiconify', 'deletecommand', 'deletefilehandler', 'destroy',
'dooneevent', 'eval', 'evalfile', 'event_add', 'event_delete',
'event_generate', 'event_info', 'exprboolean', 'exprdouble',
'exprlong', 'exprstring', 'focus', 'focus_displayof', 'focus_force',
'focus_get', 'focus_lastfor', 'focus_set', 'focusmodel', 'frame',
'geometry', 'getboolean', 'getdouble', 'getint', 'getvar',
'globalcall', 'globaleval', 'globalgetvar', 'globalsetvar',
'globalunsetvar', 'grab_current', 'grab_release', 'grab_set',
'grab_set_global', 'grab_status', 'grid', 'grid_bbox',
'grid_columnconfigure', 'grid_location', 'grid_propagate',
'grid_rowconfigure', 'grid_size', 'grid_slaves', 'group',
'iconbitmap', 'iconify', 'iconmask', 'iconname', 'iconposition',
'iconwindow', 'image_names', 'image_types', 'interpaddr',
'keys', 'lift', 'loadtk', 'lower', 'mainloop', 'master',
'maxsize', 'merge', 'minsize', 'nametowidget', 'option_add',
'option_clear', 'option_get', 'option_readfile', 'overrideredirect',
'pack_propagate', 'pack_slaves', 'place_slaves', 'positionfrom',
'propagate', 'protocol', 'quit', 'readprofile', 'record',
'register', 'report_callback_exception', 'resizable', 'rowconfigure',
'selection_clear', 'selection_get', 'selection_handle',
'selection_own', 'selection_own_get', 'send', 'setvar', 'size',
'sizefrom', 'slaves', 'split', 'splitlist', 'state', 'title',
'tk', 'tk_bisque', 'tk_focusFollowsMouse', 'tk_focusNext',
'tk_focusPrev', 'tk_menuBar', 'tk_setPalette', 'tk_strictMotif',
'tkraise', 'transient', 'unbind', 'unbind_all', 'unbind_class',
'unsetvar', 'update', 'update_idletasks', 'wait_variable',
'wait_visibility', 'wait_window', 'waitvar', 'wantobjects',
'willdispatch', 'winfo_atom', 'winfo_atomname', 'winfo_cells',
'winfo_children', 'winfo_class', 'winfo_colormapfull',
'winfo_containing', 'winfo_depth', 'winfo_exists', 'winfo_fpixels',
'winfo_geometry', 'winfo_height', 'winfo_id', 'winfo_interps',
'winfo_ismapped', 'winfo_manager', 'winfo_name', 'winfo_parent',
'winfo_pathname', 'winfo_pixels', 'winfo_pointerx',
'winfo_pointerxy', 'winfo_pointery', 'winfo_reqheight',
'winfo_reqwidth', 'winfo_rgb', 'winfo_rootx', 'winfo_rooty',
'winfo_screen', 'winfo_screencells', 'winfo_screendepth',
'winfo_screenheight', 'winfo_screenmmheight', 'winfo_screenmmwidth',
'winfo_screenvisual', 'winfo_screenwidth', 'winfo_server',
'winfo_toplevel', 'winfo_viewable', 'winfo_visual', 'winfo_visualid',
'winfo_visualsavailable', 'winfo_vrootheight', 'winfo_vrootwidth',
'winfo_vrootx', 'winfo_vrooty', 'winfo_width', 'winfo_x', 'winfo_y',
'withdraw', 'wm_aspect', 'wm_attributes', 'wm_client',
'wm_colormapwindows', 'wm_command', 'wm_deiconify', 'wm_focusmodel',
'wm_frame', 'wm_geometry', 'wm_grid', 'wm_group', 'wm_iconbitmap',
'wm_iconify', 'wm_iconmask', 'wm_iconname', 'wm_iconposition',
'wm_iconwindow', 'wm_maxsize', 'wm_minsize', 'wm_overrideredirect',
'wm_positionfrom', 'wm_protocol', 'wm_resizable', 'wm_sizefrom',
'wm_state','wm_title', 'wm_transient', 'wm_withdraw']

Bu liste alfabe sırasına göre dizilmiştir. Dolayısıyla bu listedeki “tk_setPalette” metodunu rahatlıkla bulabilirsiniz...

Gördüğünüz gibi, Tk() sınıfının bir yığın metodu var... İşte tk_setPalette() metodu da bunlardan biri olup, Tkinter pencerelerinin renk şemasını değiştirmemizi sağlıyor...

Şimdi şu örneğe bakalım:

#!/usr/bin/env python
#-*-coding:utf-8-*-

from Tkinter import *

pencere = Tk()

pencere.tk_setPalette("#D0A9F5")

etiket = Label(text="Hangi GNU/Linux Dağıtımını Kullanıyorsunuz?")
etiket.pack(pady=10,padx=10)

liste = Listbox()
liste.pack(side=LEFT)

GNULinux = ["Debian","Ubuntu","Kubuntu","RedHat"]

for i in GNULinux:
    liste.insert(END,i)

dugme = Button(text="KAPAT",command=pencere.quit)
dugme.pack()

mainloop()

Gördüğünüz gibi, tk_setPalette() metodu ile pencerenin renk şemasını değiştirdiğimizde, bundan pencere ile birlikte üzerindeki bütün pencere araçları da etkileniyor. Peki biz pencere araçlarının pencereden farklı bir renkte olmasını istersek ne yapacağız? Çok basit:

#!/usr/bin/env python
#-*-coding:utf-8-*-

from Tkinter import *

pencere = Tk()

pencere.tk_setPalette("#D0A9F5")

etiket = Label(text="Hangi GNU/Linux Dağıtımını Kullanıyorsunuz?",
               fg="#B4045F")

etiket.pack(pady=10, padx=10)

liste = Listbox()
liste.pack(side=LEFT)

GNULinux = ["Debian","Ubuntu","Kubuntu","RedHat"]

for i in GNULinux:
    liste.insert(END,i)

dugme = Button(text="KAPAT",
               bg="#610B5E",
               fg="white",
               command=pencere.quit)

dugme.pack()

mainloop()

Gördüğünüz gibi, farklı renkte olmasını istediğimiz pencere araçlarının renklerini, daha önce öğrendiğimiz seçenekler yardımıyla açık açık belirtiyoruz... Tabii sizin benden daha zevkli renk kombinasyonları seçeceğinizi tahmin ediyorum...

Böylelikle bir Tkinter programının renk şemasını nasıl değiştireceğimizi öğrenmiş olduk... Bir sonraki bölümde Tkinter uygulamaları üzerinde resimleri/simgeleri nasıl kullanabileceğimizi göreceğiz. Neticede resimler ve simgeler de bir programı güzelleştiren öğelerdir...

5.2. Pencere Araçlarına Simge Eklemek

Bu bölümde, Tkinter’de yazdığımız programlara nasıl simge ekleyebileceğimiz göreceğiz. Bu iş için ImageTk adlı modülü kullanacağız. Bu modülü kullanmak için “python-imaging” adlı paketi yüklemeniz gerekecektir. GNU/Linux kullanıcıları kendi dağıtımlarının paket yöneticisini kullanarak gerekli modülü kurabilirler. Windows kullanıcıları ise http://www.pythonware.com/products/pil/ adresini ziyaret ederek, kullandıkları Python sürümüne uygun programı indirebilir...

Gelelim asıl konumuza... Öncelikle aşağıdaki gibi bir başlangıç yapalım:

# -*- coding: utf-8 -*-

from Tkinter import *
import ImageTk

Böylelikle gerekli bütün modülleri içe aktarmış olduk. Burada “ImageTk” adlı modülü de içe aktardığımıza dikkat edin. Devam ediyoruz...

pencere = Tk()

simge = ImageTk.PhotoImage(file="simge.png")

Yukarıdaki kodlar yardımıyla önce bir pencere oluşturduk her zamanki gibi. Ardından da, programın en başında içe aktardığımız ImageTk adlı modülün PhotoImage adlı metodundan ve file adlı seçeneğinden yararlanarak, bilgisayarımızdaki “simge.png” adlı dosyayı programımız içinde kullanılabilir hale getirdik. Dikkat edin, “kullandık,” demiyorum. “Kullanılabilir hale getirdik,” diyorum... Yani kabaca ifade etmek gerekirse, bu şekilde bir “resim nesnesi” oluşturmuş oluyoruz. Devam edelim:

dugme = Button(text="Resim1",
               image=simge,
               compound="top")
dugme.pack()

mainloop()

Burada bildiğimiz şekilde, “Button” pencere aracını kullanarak bir “düğme” oluşturduk. Ama burada bilmediğimiz birkaç şey var. Hemen anlatalım: Öncelikle “image” adlı yeni bir seçenek görüyoruz kodlarımız arasında. Buraya, yukarıda “kullanılabilir hale getirdiğimiz” resim nesnesini yerleştiriyoruz. Bu seçeneğin ardından, “compound” adlı başka bir seçenek daha geliyor. Bu da “text” ve “image” seçenekleri ile tanımlanan öğelerin pencerede nasıl görüneceğini belirliyor. Burada kullandığımız “top” değeri, image seçeneğiyle gösterilen öğenin, text seçeneği ile gösterilen öğenin üstünde yer alacağını ifade ediyor. Bu “compound” seçeneği dört farklı değer alabilir:

  1. top => resim metnin üstünde
  2. bottom => resim metnin altında
  3. right => resim metnin sağında
  4. left => resim metnin solunda

Biz örneğimizde “top” değerini tercih ettik. Tabii ki siz bu dört seçenek içinden istediğinizi kullanabilirsiniz... Yukarıdaki kodlarda görünen “pack()” ve “mainloop” artık adımız soyadımız gibi bildiğimiz şeyler. O yüzden üzerinde durmuyoruz.

Şimdi isterseniz bu kodları topluca görelim:

# -*- coding: utf-8 -*-

from Tkinter import *
import ImageTk

dugme = Button(text="Resim1",
               image=simge,
               compound="top")
dugme.pack()

mainloop()

Böylece Tkinter’de resimleri ve metinleri bir arada nasıl kullanacağımızı öğrenmiş olduk. Şimdi gelin isterseniz yukarıdaki bilgilerimizi kullanarak örnek bir arayüz yazalım:

# -*- coding: utf-8 -*-

from Tkinter import *
import ImageTk

pencere = Tk()
pencere.tk_setPalette("#CECEF6")

cerceve1 = Frame()
cerceve1.grid(row=0, column=0, pady=5, padx=5)

cerceve2 = Frame()
cerceve2.grid(row=1, column=0, pady=10, padx=10)

simge1 = ImageTk.PhotoImage(file="simge1.png")
simge2 = ImageTk.PhotoImage(file="simge2.png")
simge3 = ImageTk.PhotoImage(file="simge3.png")
simge4 = ImageTk.PhotoImage(file="simge4.png")

etiket = Label(cerceve1,
               text="Kendinize Uygun bir Simge Seçiniz...",
               fg="#610B0B",
               font="Verdana 10 bold")

etiket.pack()

dugme1 = Button(cerceve2,
                text="Resim1",
                image=simge1,
                compound="top")

dugme1.grid(row=3, column=0, padx=6)

dugme2 = Button(cerceve2,
                text="Resim2",
                image=simge2,
                compound="top")

dugme2.grid(row=3, column=3, padx=6)

dugme3 = Button(cerceve2,
                text="Resim3",
                image=simge3,
                compound="top")

dugme3.grid(row=3, column=6, padx=6)

dugme4 = Button(cerceve2,
                text="Resim4",
                image=simge4,
                compound="top")

dugme4.grid(row=3, column=9, padx=6)

Bu programı çalıştırdığımızda şöyle bir görüntü elde ediyoruz:

_images/arayuz.png

Tabii bu programın düzgün çalışması için resimlerinizle program dosyasını aynı klasöre koymayı unutmuyorsunuz... Eğer resimlerle program dosyası ayrı klasörlerde duracaksa simge öğelerini belirlerken resimlerin bulunduğu klasör adlarını tam olarak yazmaya dikkat etmelisiniz...

Burada düğmeleri konumlandırmak için “pack()” geometri yöneticisi yerine “grid()” adlı geometri yöneticisini kullandığımıza dikkat edin. Öğeleri bu şekilde sıralamak için grid() yöneticisi daha uygundur. Ayrıca yukarıdaki kodlarda iki farklı “Frame” oluşturduğumuza da dikkat edin. “Label” adlı pencere aracımızı bir “Frame” üzerine, geri kalan pencere araçlarımızı (yani düğmelerimizi) ise öteki “Frame” üzerine yerleştirdik. İki farklı “Frame” kullanmamız sayesinde aynı program içinde hem “grid()” hem de “pack()” adlı geometri yöneticilerini bir arada kullanma imkanı elde ettik. İki farklı “Frame” kullanmamız aynı zamanda bize, pencere araçlarımızı daha özgür bir şekilde konumlandırma imkanı da sağladı. Ayrıca grid() yöneticisinin “padx” seçeneğini kullanarak pencere araçlarının birbirlerine olan uzaklıklarını da ayarlayabildik. Yukarıdaki kodlarla ilgili olarak anlamadığınız noktalar olması durumunda bana nasıl ulaşabileceğinizi biliyorsunuz...

Böylelikle bu konuyu da bitirmiş olduk. Bir sonraki bölümde Tkinter ile yazdığımız pencere araçlarına nasıl ipucu metni (tooltip) ekleyeceğimizi göreceğiz.

5.3. Pencere Araçlarına İpucu Metni (Tooltip) Eklemek

Fareyi pencere araçlarının üzerine getirdiğimizde ekranda beliren ve o aracın ne işe yaradığını kısaca açıklayan ufak metin parçalarına ipucu metni (tooltip) diyoruz. İşte bu bölümde Tkinter’deki pencere araçlarına nasıl ipucu metni ekleyeceğimizi öğreneceğiz. Öncelikle şunu söyleyelim: Tkinter böyle bir özelliği bize kendiliğinden sunmuyor. Ama hem Python’un hem de Tkinter’in en güçlü yanlarından biri, dilde olmayan bir özelliği üçüncü parti yazılımlar aracılığıyla dile ekleyebilmemizdir... Pencere araçlarına ipucu metni ekleyebilmek için bu üçüncü parti yazılımlardan birini kullanacağız. Şu noktada yapmamız gereken ilk iş, http://svn.effbot.org/public/stuff/sandbox/wcklib/ adresinden wckToolTips adlı modülü bilgisayarımıza indirmek olacaktır. Bu modül pencere araçlarına ipucu metni eklememizi sağlayacak. Bu modülü, içinde ipucu metni kullanacağımız programlarımızla aynı klasör içinde tutmamız gerekiyor. Örneğin, diyelim ki falanca.py adında bir program yazdık. Bu programın “FALANCA” adlı klasör içinde yer aldığını varsayarsak, wckToolTips modülünü falanca.py ile aynı klasör içine, yani “FALANCA” adlı klasöre atmamız gerekiyor. Bu sayede, wckToolTips modülünü programımız içine aktarırken (import) herhangi bir sorun yaşamayacağız. İsterseniz bununla ilgili ufak bir örnek yapalım:

from Tkinter import *
import wckToolTips

Böylece Tkinter modülüyle birlikte wckToolTips modülünü de içe aktarmış olduk. Python, biz bu modülü içe aktarmaya çalıştığımızda, öncelikle programımızın içinde bulunduğu dizini kontrol ederek, wckToolTips.py adlı bir dosya olup olmadığına bakacaktır. Eğer modül bu dizinde bulunamaz ise, Python sys.path çıktısında görünen dizinlerin içini de denetleyecektir. Eğer modül orada da yoksa, Python bize bir hata mesajı gösterecektir. Bu durumda, wckToolTips gibi harici bir modülü programın bulunduğu dizin içine atmak en kolay yol olacaktır. Neyse... Biz örneğimize devam edelim:

pencere = Tk()

etiket = Label(text="Gölgelerin gücü adına!")
etiket.pack()

dugme = Button(text="Tamam", command=pencere.destroy)
dugme.pack()

Burada normal bir şekilde, penceremizi tanımladık. Bunun yanında, bir adet etiket, bir adet de düğme oluşturduk. Yeni bir şey yok... Devam edelim:

wckToolTips.register(dugme, "Titrek'i Atılgan'a çevirir!")

mainloop()

İşte wckToolTips modülü burada devreye giriyor. Bu modülün “register” adlı metodunu kullanarak, “dugme” adlı pencere aracının üzerine fare ile geldiğimizde ekranda görünmesini istediğimiz ipucu metnini oluşturuyoruz. Burada ipucu metnimiz şu: “Titrek’i Atılgan’a çevirir!” (Hey gidi günler!)

Programımızı bütünüyle bir görelim bakalım:

# -*- coding: utf-8 -*-

from Tkinter import *
import wckToolTips

pencere = Tk()

etiket = Label(text="Gölgelerin gücü adına!")
etiket.pack()

dugme = Button(text="Tamam", command=pencere.destroy)
dugme.pack()

wckToolTips.register(dugme, "Titrek'i Atılgan'a çevirir!")

mainloop()

Gördüğünüz gibi, duğmenin üzerine fare ile geldiğimizde ipucu metnimiz ekranda görünüyor... İşte Tkinter’de ipucu metinlerini kullanmak bu kadar kolaydır. Kendi yazdığınız programlarda bu modülü kullanarak, programınızı kullanıcılar açısından daha cazip ve anlaşılır bir hale getirebilirsiniz. Gelin isterseniz bununla ilgili bir örnek daha yapalım:

# -*- coding: utf-8 -*-

from Tkinter import *
import wckToolTips
import time

pencere = Tk()

etiket = Label(text="Saat 17:00'da randevunuz var!")
etiket.pack(expand=YES, fill=BOTH)

def saat(pencere_araci, fonksiyon):
    return "Şu anda saat: %s"%time.strftime("%H:%M:%S")

dugme = Button(text="Tamam", command=pencere.destroy)
dugme.pack(expand=YES)

wckToolTips.register(pencere, saat)

mainloop()

Burada ipucu metnimizi doğrudan pencerenin kendisine atadık. Böylece fare ile pencere üzerine geldiğimizde o an saatin kaç olduğu ekranda görünecektir... Ayrıca burada register metoduna argüman olarak bir fonksiyon atadığımıza dikkat edin. Yukarıda yazdığımız kodları şöyle yazarsak ne olur peki?

# -*- coding: utf-8 -*-

from Tkinter import *
import wckToolTips
import time

pencere = Tk()

etiket = Label(text="Saat 17:00'da randevunuz var!")
etiket.pack(expand=YES, fill=BOTH)

dugme = Button(text="Tamam", command=pencere.destroy)
dugme.pack(expand=YES)

wckToolTips.register(pencere, "Şu anda saat: %s"%time.strftime("%H:%M:%S"))

mainloop()

Bu şekilde, programımız yine çalışır ve hatta saatin kaç olduğunu ekranda ipucu metni olarak da gösterir. Ama bir farkla: Bu yazım şeklinde ekranda görünen saat durağan olacaktır. Yani program kapatılıp tekrar açılana kadar hep aynı saati gösterecektir. Ama register metoduna bir önceki örnekte olduğu gibi, bir fonksiyon atarsak saat devinecek, yani pencere üzerine ikinci kez geldiğimizde ipucu metni saati güncellenmiş olarak verecektir.

Bu son kodlarda, ayrıca pack() geometri yöneticisinin “expand” ve “fill” seçeneklerini kullanarak pencere boyutlandırmaları sırasında pencere araçlarının uzayıp kısalmasını sağladık. Bu kodları bir de bu seçenekler olmadan çalıştırırsanız ne demek istediğim daha bariz bir şekilde ortaya çıkacaktır.

Son olarak, yukarıdaki kodlarda, “saat” adlı fonksiyonun iki argüman aldığına dikkat edin. Bu fonksiyonu argümansız olarak yazarsak programımız çalışsa da ipucu metni görüntülenmeyecektir. Böyle bir durumda ipucu metninin görüntülenmemesine yol açan hatayı komut satırı çıktılarından takip edebilirsiniz.