Python ile wxWidgets'a Giriş

Python'un platformdan bağımsız bir dil olmasından dolayı onun ile yazacağımız kullanıcı arabirimine sahip programların da platformdan bağımsız olması kaçınılmaz bir özelliktir. Sanırım buradaki sihri hepimiz fark etmiş olacağız ki şu an bu dökümanı okumaktasınız. Yazdığı tek bir kodun birçok platformda, hiçbirinde herhangi bir fark olmadan çalışabilecek olması şüphesiz ki bir programcı için arzu edilebilecek en önemli özelliklerden biridir.

Python ile öntanımlı gelen Tkinter modülü ile her platformda çalışabilecek UI'ler (User Interface / Kullanıcı Arayüzü) [2] yazabileceğimiz halde, benim burada wxWidgets kütüphanesini seçmemin sebebi, bu kütüphanenin çok daha gelişmiş bir esneklik ve özellikler yelpazesi sunmasından kaynaklanıyor.


Yasal Uyarı

Bu belgeyi, Free Software Foundation tarafından yayınlanmış bulunan GNU Genel Kamu Lisansının 2 ya da daha sonraki sürümünün koşullarına bağlı kalarak kopyalayabilir, dağıtabilir ve/veya değiştirebilirsiniz. Bu lisansın bir kopyasını http://www.gnu.org/copyleft/gpl.html adresinde bulabilirsiniz.

Bu belgedeki bilgilerin kullanımından doğacak sorumluluklar, ve olası zararlardan belge yazarı sorumlu tutulamaz. Bu belgedeki bilgileri uygulama sorumluluğu uygulayana aittir.

Tüm telif hakları aksi özellikle belirtilmediği sürece sahibine aittir. Belge içinde geçen herhangi bir terim bir ticarî isim yada kuruma itibar kazandırma olarak algılanmamalıdır. Bir ürün ya da markanın kullanılmış olması ona onay verildiği anlamında görülmemelidir.


İçindekiler

I. Neden wxWidgets?
II. Başlamadan Önce
III. Performans Hakkında
IV. Hello, World!
V. Nesne Yerleşimi
VI. Bir Kaç Yeni Bileşen
VII. Nesne Referansları
 
A. Notlar
B. Kaynakça
C. Teşekkürler

I. Neden wxWidgets? [Y, A]

TODO

II. Başlamadan Önce [Y, A]

Elbette tüm bu dökümanı okumaya geçmeden önce elimizde aşağıda kullanacağımız yazılımların olması gerektiğinde hemfikiriz sanırım. Bunun için ilk önce kullanacağımız sistemde Python kurulu olmalı. Python'u http://www.python.org/download/ adresinden indirip sisteminize kurmanız gerekmekte. Ardından da kullanacağımız wxPython kütüphanesini http://wxpython.org/download.php adresinden edinebilirsiniz. Dökümanın bundan sonraki kısmında yukarıda belirttiğim yazılımları sorunsuz kurup, çalıştırabildiğinizi varsayacağım. (Bunu öğrenmek için python komutu satırında import wxPython komutunun çalışıp çalışmadığına bakmanız yeterli.) Bütün bunların dışında, bu dökümanın amacı size Python dilini öğretmekten öte, dili wxWidgets ile kullanarak nasıl UI geliştirebileceğiniz yönünde olacak. Bu nedenle önceden az da olsa bir Python altyapınızın olması şart.

III. Performans Hakkında [Y, A]

"Fakat yorumlanan diller çok verimli çalışmazlar. Program büyüdükçe bir hantallaşma oluşur." gibi bir fikre kapılabilirsiniz. Ve bu konuda bir noktaya kadar hak sahibisiniz. Diğer yandan, performansın önem kazandığı bir durumda program zaten C/C++ ile yazılıp Python koduna dahil edilebilir ve bu inanılmayacak derecede kolay bir işlemdir. Bu nedenden dolayı Python çoğu zaman program ile onun grafik arabirimi arasında bir yapıştırıcı rolü oynar. Ve açıktır ki Python bu konuda oldukça etkin bir yapıya sahiptir. Python kullanarak çok kolay bir şekilde, nesne tabanlı bir programala dili olduğundan, haftalar sonra kod tekrar okunduğunda dahi rahatça anlaşılabilecek grafik arabirimine sahip programlar yazabilirsiniz. Bütün bunlara ek olarak Python ile yazacağınız programda (pratikte çok az da olsa) kaybettiğiniz performansı, arabirimi yazarken aldığınız verim ve esneklik ile kapatabilirsiniz. [2]

Buna ek olarak Python'da performans arttırımı için yazdığınız programı C ya da x86 Assembly koduna kolaylıkla çevirebilirsiniz. Bunun hakkında daha ayrıntılı bilgi için http://www.python.org/doc/faq/programming.html#id6 adresine bakabilirsiniz.

IV. Hello, World! [Y, A]

Her programlama dilinin vazgeçilmez başlangıç noktası olan "Hello, World!" programı ile wxPython için hafif bir giriş yapalım.

#!/usr/bin/python
# -*- coding: iso-8859-9 -*-

from wxPython.wx import *

class MainFrame(wxFrame):
    def __init__(self, parent, fid, title):
        wxFrame.__init__(self, parent, fid, title)

        ID_BUT = wxNewId()
        self.but = wxButton(self, ID_BUT, "Merhaba, &Dünya!")
        EVT_BUTTON(self, ID_BUT, self.onButClick)

        self.Show(True)

    def onButClick(self, event):
        self.Close(True)


if __name__ == "__main__":
    app = wxPySimpleApp()
    frame = MainFrame(None, wxID_ANY, "My First Frame")
    app.MainLoop()

Bu kodu dosyaya kaydedip çalıştırdığımız zaman karşımıza üzerinde "Hwllo, World!" yazan bir butonun çıkacağı aşikardır. Bu basit programcığı (yoksa betik mi demeliydik) daha anlaşılabilir kılmak için şematize etmek için aşağıdaki gibi bir şekil kullanabiliriz.

01

Programın kaynak kodundaki satırları açıklamaya başlamadan önce neler yapabildiğimize bakalım. "Merhaba, Dünya!" yazan butona tıkladığımız zaman pencerenin kapanıp programdan çıktığımızı sanırım herkes görmüştür. Bunun dışında pencere aktifken Alt+D tuş kombinasyonunu oluşturduğumuzda yine butona tıklanmış gibi davranıldığına da dikkat edin. Bunun nedeni butonun başlığını yazarken "World" kelimesindeki `w' harfinden hemen önce kullandığımız `&' karakteri. Bu yöntemi bir çok wxWidgets nesnesine uygulayabilirsiniz (butonlar, menü elemanları...).

Programın #!/usr/bin/python satırında çalıştırdığımız uygulamanın hangi program tarafından yorumlanacağını seçtikten sonra # -*- coding: iso-8859-9 -*- satırı yazacağımız kodda kullanacağımız karakter setini göstermekte. Bunun ile herhangi başka bir şey ile uğraşmadan programınızın istediğiniz herhangi bir yerinde türkçe karakter kullanabilirsiniz (Her ne kadar ben bu örnekte hiç türkçe karakter kullanmamış olsam da "Hello, World!" yerine "İşte türkçe karakterler: ıİşŞğĞ" yazabilirdim). from wxPython.wx import * satırı ile de arabirim tasarım ve etkileşiminde kullanacağımız nesneleri çağırıyoruz.

Tüm bunlardan sonra ilk olarak `class' nesnemizi oluşturarak işe başlıyoruz. Nesne tabanlı bir programlama dilinin nimetlerini sonuna kadar kullanmakta yarar var diyerekten, wxPytnon kullanarak yaptığımız çoğu arabirimde her bir form (yada pencere) bir `class' olarak tanımlanacak ve wxPython.wx modülünden wxFrame nesnesi `inherit' (miras) edilerek kullanılacaktır. Aslında wxFrame nesnesi sizin için her şeyi yapmıştır. Bizim yapmamız gereken sadece wxFrame'in __init__ fonksiyonu ile ilgili değişikleri yapıp, oluşturduğumuz `class'ı çağırmak olacaktır. Nitekim de yukarıdaki kodda yaptığımız bundan farklı bir şey değil. wxFrame.__init__(self, parent, fid, title) satırı ile kullanıcının girdiği argümanlara (parent, id, title) göre çerçevenin algılanmasını sağlıyoruz.

Oluşturduğumuz her bir nesne kendine has bir tamsayı `ID'ye sahip olacaktır. Bunu istersek kendimiz girebileceğimiz gibi burada wxNewId() fonksiyonunu bize yardım etmesi için çağırabiliriz. Bu sayede herhangi bir zahmete girmeden wxNewId() herseferinde bize kendine has yeni nesne `ID'leri döndürecektir. ID_BUT = wxNewId() satırı ile oluşturacağımız buton için bir `ID' tanımladıktan sonra butonumuzu self.but = wxButton(self, ID_BUT, "Merhaba, &Dünya!") satırı ile oluşturuyoruz. (Burada kullandığım fonksiyonlarda, wxButton(), wxFrame.__init__() olsun, sadece gerekli parametreleri kullanıyorum. Bunun dışında daha birçok parametre kullanabilirsiniz.) Burada butonu atadığım self.but değişkenin adının hiçbir önemi yok, yalnızca bu `class'a ait olduğunu belirtmek için başında yer alan self. ifadesi yer almak zorunda o kadar.

wxWidgets'da oluşturduğumuz nesnelerin çalışma mantığı ilişkilendirmeler halinde gerçekleşir. Yani çalışan her nesne bir olay (fonksiyon) ile ilişkilendirilmiştir. Burada biz de oluşturduğumuz butonu, yine kendimizin oluşturduğu bir onButClick fonksyionu ile ilişkilendiriyoruz. Buradaki EVT_BUTTON(self, ID_BUT, self.onButClick) satırı bu ilişkilendirmede bize yardım ediyor. Dikkat edilmesi gereken nokta EVT_BUTTON fonksiyonunun rasgele bir fonksiyon olmaması. Yani örnek verecek olursak ileride kullanacağımız Menu nesnesi için EVT_MENU fonksiyonunu kullanacağız.

Programdaki tüm nesneleri belirtip gerekli ilişkilendirmeleri yaptıktan sonra sıra oluşturulan `frame'in ekrana yazdırılmasına geldi. Burada da self.Show(True) fonksiyonu yardımımıza koşuyor. Benzer şekilde onun hemen altında, self.but tarafından kullanılacak olan, onButClick fonksiyonunda yer alan self.Close(True) ifadesinin de görevi `frame'in kapatılması oluyor.

Açılacak penceremiz için gerekli `class'ı oluşturduktan sonra sıra tüm bunları çalıştırmaya geldi. Python'da her ifadenin bir nesne teşkil ettiği unutulmayarak wxPython.wx modulünden bu sefer wxPySimpleApp() fonksiyonunu app değişkenine atayarak asıl pencere nesnemizi oluşturuyoruz (Daha önce arabirim programla ile uğraşmış olanlar en baştan beri neden pencere (`window') ve çerçeve (`frame') sözcüklerini özellikle aynı anlamda kullanmaktan çekindiğimi anlayacaklardır.) Ardından MainFrame `class'ımızı app'ye tanıtıp programı MainLoop'u çağırarak başlatıyoruz.

Bu kadar basit bir örnek için çok fazla açıklamada bulunduğumu düşünebilirsiniz, ama olayın en basit hatlarının anlaşılır kılındığı taktirde ilerki adımlarda hiç sorun çıkmadan ilerlenebileceği taraftarlarındanım.

V. Nesne Yerleşimi [Y, A]

Bir önceki örnekte tek bir bileşen kullandığımız için onun çerçeve üzerinde yerleşmi bizim için bir sorun oluşturmadı. Fakat bileşen sayısı arttığında bunların form üzerinde yerleştirilmesi gibi bir sorun karşımıza çıkıyor. Bunu aşmak için izlenebilecek iki yol var:

  1. Her bir bileşen için önceden belirli (`fixed') bir yer tanımlamak ve bu şekilde hepsini, çerçeveyi bir xy düzlemi gibi düşünerek yerleştirmek.
  2. Çerçeveyi satır ve sütunlara bölerek (SGML tabanlı dillerdeki tablo mantığına benzer şekilde), bileşenleri bu tabloların hücrelerine yerleştirmek.

İlk seçenek, genelleme yapacak olursak, (kendi kanımca) hiç kullanılmaması gereken bir opsiyon oluşturuyor. Çünkü pencerelerin aktif birer çerçeve olduğu düşünülürse, büyütülüp kücültülmesi gibi bir durumda çok çirkin bir görüntü oluşacaktır. Bundan öte yerleştireceğim her nesne için birer koordinat girmek sanırım kendime çektireceğim en son işkence olurdu. Birinci seçeneği bu kadar yere çalmamdan da anlayacağınız üzere bu dökümanda ilk seçenek hakkında bilgi vermeyeceğim.

Ben her ne kadar kendisini tabloya benzetsem de wxWidgets'da nesnelerin dinamik yerleşimi için `Sizer'lar kullanılmakta. Bunlar ile yarattığımız dik ya da yatay hücrelerin içlerine istediğimiz nesneyi yerleştirebiliyoruz. Sizer'lar; wxBoxSizer, wxGridSizer ve wxFlexGridSizer şeklinde üç çeşit olup, ben burada wxBoxSizer'ın kullanımından bahsedeceğim [3]. Lafı fazla uzatmadan bir örnek ile konuya girmek istiyorum:

#!/usr/bin/python
# -*- coding: iso-8859-9 -*-

from wxPython.wx import *

class MainFrame(wxFrame):
    def __init__(self, parent, fid, title):
        wxFrame.__init__(self, parent, fid, title, \
            pos = (200, 200), \
            size = (400, 200), \
            style = wxDEFAULT_FRAME_STYLE \
                |wxNO_FULL_REPAINT_ON_RESIZE)

        self.butList = []
        for i in range(0, 5):
            self.butList.append(wxButton(self, i, "Button &" + `i`))

        self.hsizer = wxBoxSizer(wxHORIZONTAL)

        self.hsizer.Add(self.butList[0], 3, wxEXPAND)

        self.vsizer = wxBoxSizer(wxVERTICAL)
        for i in range(1, 4):
            self.vsizer.Add(self.butList[i], 1, wxEXPAND)
        self.hsizer.Add(self.vsizer, 1, wxEXPAND)

        self.sizer = wxBoxSizer(wxVERTICAL)
        self.sizer.Add(self.hsizer, 2, wxEXPAND)
        self.sizer.Add(self.butList[4], 1, wxEXPAND)

        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.sizer.Fit(self)

        self.Show(True)

if __name__ == "__main__":
    app = wxPySimpleApp()
    frame = MainFrame(None, wxID_ANY, "My First Frame")
    app.MainLoop()

Yazdığımız ufak betiği şematize edip onun üzerinde ne yaptığımızı anlatmaya çalışacağım.

02

Yukarıdaki şekilden de anlaşılacağı üzere yaptığımız aslında çok basit bir şey: Değişken isimleri sizer, vsizer ve hsizer olmak üzere iki tane dik (vertical) ve bir tane de yatay (horizontal) `Sizer' oluşturduk. Sonra vsizer'a üç, diğerlerine de birer adet olacak şekilde, her birinin içine bir tane buton yerleştirdik.

Burada dikkatinizi çekmek istediğim noktalardan biri `Sizer'ların Add() fonksiyonlarına girdiğimiz parametreler. Bu parametrelerden ilki, nesnemizi hangi `Sizer'a yerleştireceğimizi söylemekte. İkinci parametre de ise nesnelerin hangi orantılar ile yerleştirileceğini seçiyoruz. Yukarıdaki şekilde de anlatmaya çalıştığım üzere hsizer'ın içini 3:1 şeklinde orantılayarak bir buton ve içinde eşit orantılara sahip 3 buton yer alan bir dik `Sizer' (vsizer) ile doldurduk. Benzer olarak sizer'ı da 2:1 oranında bir diğer `Sizer' (hsizer) ve buton ile doldurduk. Eklemek istediğim diğer bir nokta ise, orantı bölümünde gireceğimiz `0' değeri ise herhangi bir boyut (size) değişiminde, nesnenin bundan etkilenmemesini sağlayacaktır. Üçüncü parametrede `Sizer'ların Add() fonksiyonuna has opsiyonlar giriyoruz. Burada kullandığım wxEXPAND'in (wxGROW ile eş görevli) anlamı `Sizer'ın buyutunun değişmesine göre nesnenin de bunun içine yayılmasını sağlıyor. Üçün parametre bölümünde bundan başka kullanabileceğimiz opsiyonları kısaca listelemeye çalışacak olursak:

__init__()'in en altında kullandığımız SetSizer() fonksiyonu ile o anki `frame'in `Sizer'ını belirledikten sonra, SetAutoLayout() ile nesnelerin otomatik yerleştirilmesini aktif hale getirip, Fit() fonksiyonu ile de her bir nesnenin kendi hücresinde alacağı boyutları hesaplattık. Bu üç komut neredeyse her `Sizer'dan sonra kullanılır.

Yazdığımız kodda dikkatimizi çeken diğer yeni bir şey ise wxID_ANY ifadesi olmalı (wxID_ANY yerine kısaca -1 değeri de girilebilirdi, fakat programın okunabilirliği açısından ileri de kodu bir daha gördüğümüz zaman wxID_ANY, bana -1'den daha çok şey ifade edecek gibi geldiğinden döküman boyunca -1 yazmak yerine wxID_ANY'yi tercih edeceğim). Burada tek bir `frame' kullanacağımız ve bunun da hiçbir etkileşimi olmayacağından wxWidgets'a bunun için herhangi bir `ID' kullanabileceğini haber verdik. Aksi taktirde, birden fazla `frame' ve bu `frame'lerden en az birinin bir etkileşime sahip olduğu bir durumda, ilişkilendirmenin hangi `frame' ile olduğunu anlayabilmek için herbirine kendine has bir `ID' vermemiz gerekecekti. Burada butonlarda hiçbir etkileşim olmadığına dikkat edelim. Bu yüzden de butonlar için hiçbir `ID' oluşturmadan direk onları yerleştirdik.

Burada yerleştirdiğimiz butonlardan ve tasarladığımız orantı şematiğinden ötürü wxBoxSizer kullandık. Eğer tam tablo hesabından bir işlem gerçekleştirecek olsaydık (mesela, alt alta iki satırdan ve üç sütündan oluşmuş bir tablo) bunun için bize daha çok kolaylık sunacak olan wxFlexGridSizer ya da wxBoxSizer nesnelerinden birini kullanırdık.

VI. Bir Kaç Yeni Bileşen [Y, A]

Aşağıda nesnelerin nasıl kullanağının daha iyi anlaşılması ve bir kaç yeni nesne kullanımına da örnek olması açısından basit bir hesap makinası yapmaya çalıştım. Kodumuz bu sefer uzun olduğundan, adımları kod üzerindeki açıklama alanları ile anlatmayı daha uygun buldum.

#!/usr/bin/python
# -*- coding: iso-8859-9 -*-

from wxPython.wx import *
from os import getcwd, path
from re import sub

class MainFrame(wxFrame):
    def __init__(self, parent, fid, title):
        wxFrame.__init__(self, parent, fid, title, \
            style = wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)

        # wxMenuBar() fonksiyonu ile oluşturduğumuz menü çubuğunu menuBar
        # değişkenine atadıktan sonra, SetMenuBar() fonksiyonu ile bunun
        # geçerli menü çubuğu olarak algılanmasını sağlıyoruz.
        menuBar = wxMenuBar()
        self.SetMenuBar(menuBar)

        # Menü oluştururken izleyeceğimiz yol da bundan önceki oluşturduğumuz
        # nesnelerden pek farklı olmayacak. Burada da yardımımıza wxMenu()
        # fonksyionu koşuyor
        fileMenu = wxMenu()
        # Her seferinde teker teker her menü elemanını listeye elle eklemek
        # istemedim. Bunun yerine bir liste halinde menü elemanlarını
        # oluşturup onları, yine kendi yazdığım bir fonksiyon olan, placeMenu()
        # adlı fonksiyona gönderip orda gerekli işlemlere tabi tuttuktan sonra
        # menuBar nesnesine ekledim.
        fileMenuTuple = ((wxNewId(), "&Open", \
            "Open a saved calculation.", self.onFileOpen),
            (wxNewId(), "&Save", "Save written calculation.", self.onFileSave),
            ("sep"),
            (wxNewId(), "E&xit", "Exit from the program.", self.onFileExit))
        self.placeMenu(menuBar, "&File", fileMenu, fileMenuTuple)

        # Yukarıdakine benzer şekilde bu sefer help (yardım)
        # menüsünü oluşturuyoruz
        helpMenu = wxMenu()
        helpMenuTuple = ((wxNewId(), "&About", \
            "About the program.", self.onHelpAbout), )
        self.placeMenu(menuBar, "&Help", helpMenu, helpMenuTuple)

        # Kullanacağımız `Sizer' nesnesini oluşturuyoruz.
        self.sizer = wxBoxSizer(wxHORIZONTAL)
        self.SetSizer(self.sizer)
        self.SetAutoLayout(1)

        # Kullanıcının hesablanacak işlemi gireceği yazı alanı
        ID_PROC = wxNewId()
        # , style=wxTE_DONTWRAP
        self.proc = wxTextCtrl(self, ID_PROC, \
            size = (250, 100), style = wxTE_DONTWRAP|wxTE_MULTILINE)
        # En fazla kaç karakterlik bir işlem girebileceğini ayarlıyoruz
        self.proc.SetMaxLength(100)
        # Oluşturduğumuz listeyi yatay `Sizer'a ekliyoruz.
        self.sizer.Add(self.proc, 1, wxEXPAND)

        # `Calculate' ve `Clear' butonları için `Sizer' oluşturuyoruz.
        self.vsizer = wxBoxSizer(wxVERTICAL)

        # `Clear' butonu oluşturuluyor
        ID_CLEAR = wxNewId()
        self.clear = wxButton(self, ID_CLEAR, "&Clear", size = (100, -1))
        self.vsizer.Add(self.clear, 1, wxEXPAND)
        EVT_BUTTON(self, ID_CLEAR, self.onClear)

        # `Calculate' butonu oluşturuluyor
        ID_CALC = wxNewId()
        self.calc = wxButton(self, ID_CALC, "C&alculate", size = (100, -1))
        self.vsizer.Add(self.calc, 2, wxEXPAND)
        EVT_BUTTON(self, ID_CALC, self.onCalc)

        # `vsizer', `subSizer'ın içine yerleştirliyor
        self.sizer.Add(self.vsizer, 0, wxEXPAND)

        # Kendimize kullanmak üzre bir de güzel bir durum çubuğu yapalım
        self.statusBar = self.CreateStatusBar()

        # İlk açılış için durum çubuğuna bir şeyler yazıyoruz
        self.statusBar.SetStatusText("Tip: Use above text field for " + \
            "your calculations.")

        # Nesnelerin boyutlarını hesaplatıyoruz
        self.sizer.Fit(self)

        self.Show(true)

    # placeMenu fonksyionu belirtilen menü çubuğuna (menuBarObj),
    # belirtilen başlıktaki (menuTitle), belirtilen menü nesnesi (menuObj)
    # yine belirtilen içerik listesiyle (menuTuple) ekliyor.
    # Ve her menü elemenını belirtilen (menuTuple[3]) `event' ile
    # ilişkilendiriyor.
    def placeMenu(self, menuBarObj, menuTitle, menuObj, menuTuple):
        for item in menuTuple:
            if item == "sep": menuObj.AppendSeparator()
            else:
                menuObj.Append(item[0], item[1], item[2])
                EVT_MENU(self, item[0], item[3])
        menuBarObj.Append(menuObj, menuTitle)

    def onFileOpen(self, event):
        # İlk önce ekrana açaçağımız `Open' penceresini tanımlıyoruz.
        dlg = wxFileDialog(self, "Open File", \
            defaultDir = getcwd(), wildcard = "*.*", style = wxOPEN)
        # Pencere açıldıktan sonra eğer kullanıcı bir dosya seçip OK'e tıkladı
        #| ise o dosyayı açmaya çalışıp onu yazı alanına yazdırıyoruz.
        if dlg.ShowModal() == wxID_OK:
            filename = dlg.GetFilename()
            dirname = dlg.GetDirectory()
            fp = open(path.join(dirname, filename))
            self.proc.SetValue(fp.read())
            fp.close()
        dlg.Destroy()

    def onFileSave(self, event):
        # Yukarıdakine benzer şekilde başka bir dialog ile kaydedeceğimiz
        # dosyayı seçip, yazı alanındaki işlemi bu dosyaya kaydetmeye
        #| çalışıyoruz.
        dlg = wxFileDialog(self, "Save File", \
            defaultDir = getcwd(), wildcard = "*.*", style = wxSAVE)
        if dlg.ShowModal() == wxID_OK:
            filename = dlg.GetFilename()
            dirname = dlg.GetDirectory()
            try:
                fp = open(path.join(dirname, filename), "w")
                fp.write(self.proc.GetValue())
                fp.close()
            except:
                # Eğer kayıt işleminde başarısız olursak bir hata
				# dialoğu ile bunu kullanıcıya bildiriyoruz.
                errDlg = wxMessageDialog(self, "There was an error occured " + \
                    "while trying to save your calculation.\nPlease check " + \
                    "your write permissions in the selected directory and " +
                    "try again.", caption = "File Cannot Be Saved!",
                    style = wxOK|wxICON_ERROR)
                errDlg.ShowModal()
        dlg.Destroy()

    def onFileExit(self, event):
        self.Close(True)

    def onClear(self, event):
        self.proc.Clear()

    def onHelpAbout(self, event):
        dlg = wxMessageDialog(self, "Simple calculator is a basic " + \
            "wxPython\nexample for a wxWidgets and Python usage tutorial.",
            style = wxOK|wxICON_INFORMATION)
        dlg.ShowModal()

    # Girilen işlemi hesaplamaya geçmeden önce işlemde doğru karakterler
    # kullanıp kullanılmadığından emin olmamız lazım.
    def checkCalc(self, calc):
        cleared = sub("[0-9]|\(|\)|\+|\%|\/|\-|\*|\.", "", calc)
        if cleared: return False
        else: return True

    # İşlemi hesaplamaya geçiyoruz
    def onCalc(self, event):
        self.statusBar.SetStatusText("Calculating given process...")
        calc = sub("\n|\r|\t", "", self.proc.GetValue())
        if calc and self.checkCalc(calc):
            result = "Success! Result = " + `eval(calc)`
        else:
            result = "Failed! Please enter a correct statement."
        self.statusBar.SetStatusText(result)


if __name__ == "__main__":
    app = wxPySimpleApp()
    frame = MainFrame(None, wxID_ANY, "Simple Calculator")
    app.MainLoop()

Yukarıdaki kodda gerekli gördüğüm yerleri açıklama alanları kullanarak bir nebze de olsa nesne hakkında bilgi verecek şekilde doldurmaya çalıştım. Bunun dışında dikkatiniz çekmek istediğim bir kaç noktaya geçmeden önce, bu kısa betiğin de işleyişini, ilerideki anlatımımda kolaylık sağlaması için, şematize etmek istedim:

03

Yazdığım koddan da anlaşılacağı üzere betik çalıştırıldığında bir menü ve durum çubuğuna sahip, bunların da ortasında 1 yazı alanı ve 2 buton yer alan bir pencere açılacaktır. Kodun, açıklama alanlarındaki bilgilerden ve dilin kendi anlaşılabilirliğinden dolayı, açık olduğunu düşündüğümden neyin ne işe yaradığını burada anlatmayacağım. Eğer hala kafasında soru işareti olanlar varsa (hatta mümkünse dökümanı okuyan herkes) kodu bir kez kendi bir dosyaya yazdıktan sonra (kopyala-yapıştır yerine lütfen kendiniz elle yazmayı deneyin, daha çok faydalı olacağı görüşündeyim) çalıştırıp bakabilir.

Bu örnekte, önceki yazdığım örneklerden farklı olarak kullandığım bir kaç nesne dışında (wxMenu, wxMenuBar, wxTextCtrl ve wxStatusBar) dikkatinizi dialoglara (wxFileDialog) çekmek istiyorum. wxWidgets'ın bize sunduğu binbir nimetten biri olan `Dialog'lar bir çok çeşitliğe sahip olup her an, her işletim sisteminde emrimize amade bir şekilde beklemektedir. Kullanabileceğimiz `Dialog' nesnelerini kısaca listeleyecek olursak: wxColourDialog, wxDialog, wxDirDialog, wxFileDialog, wxFindDialogEvent, wxFindReplaceDialog, wxFontDialog, wxInitDialogEvent, wxMessageDialog, wxMultipleChoiceDialog, , , wxPageSetupDialog, wxPrintDialog, wxProgressDialog, wxSingleChoiceDialog ve wxTextEntryDialog.

VII. Nesne Referansları [Y, A]

wxPython, wxWidgets için size bir API sunduğundan kullanacağınız fonksiyonlar wxWidgets'da varolanların sadece Python için uyarlanmışı şeklinde düşünülebilir. Bu nedenden dolayı wxPython için her bileşen ve sınıfın ayrıntılı bir şekilde anlatıldığı bir referans yer almamakta. Bunun yerine wxWidgets kütüphane dökümantasyonunda gördüğünüz her bileşen referansını C++'dan Python'a uyarlayarak kullanabilirsiniz (Python ile C++'ın ayrı düştüğü noktalarda dökümantasyona zaten not düşülmüştür). Tüm bunlara ek olarak çoğu komut ve değişken hakkında ayrıntılı bilgi için Python'da da sık sık yaptığımız gibi dir(<function>) ve <function>.__doc__ yöntemlerine de başvurabiliriz.

Ayrıyetten web'de bulduğum güzel bir sayfada kullanabilceğiniz tüm `event' (EVT_*) fonksiyonları ayrıntılı olarak listelenmiş. Bu uzun listeye http://www.bzzt.net/~wxwindows/xmldocs/generating/eventslist/eventslist.html adresinden ulaşabilirsiniz. Ayrıca wxWidgets'da yer alan tüm nesnelerin listesine ise yine wxWidgets'ın kendi dökümantasyonunda yer alan http://www.wxwindows.org/manuals/2.5.1/wx_classref.html adresinden ulaşabilirsiniz.

Tüm bu belirttiğim referans noktalarında da verilen örnekler aşağı yukarı birbirinin aynıdır. Bundan sonra wxWidgets deneyiminizi arttırmanız için yapmanız gereken diğer programların kaynak kodlarına ve (wxWidgets'ın dökümantasyonunda yer alan) nesne referanslarına bakarak bol bol yapıp tekrar tekrar bozarak bir şeyler üretmeye çalışmaktan geçiyor.

A. Notlar [Y, A]

  1. Python ile kullanabileceğiniz pencere kütüphaneleri hakkında daha ayrıntılı bilgi için Python'un kendi dökümantasyonunda yer alan http://www.python.org/doc/faq/gui.html#what-platform-independent-gui-toolkits-exist-for-python adresine göz atabilirsiniz.
  2. http://www-106.ibm.com/developerworks/library/l-wxpy/index.html adresindeki dökümantasyonda yer alan bir bölümden özet olarak çevrilmiştir.
  3. `Sizer'lar hakkında ayrıntılı bilgi için wxWidgets dökümantasyonunda yer alan Sizer Overview başlıklı yazıya bakabilirsiniz. `Sizer'lar için A'dan Z'ye güzel bir tanım ve kullanım klavuzu teşkil etmekte.

B. Kaynakça [Y, A]

C. Teşekkürler [Y, A]