original in fr Hilaire Fernandes
fr to en Lorne Bailey
en to tr Esen Fahri AKIN
Hilaire Fernandes Gnome masaüstü için "ücretsiz" eğitsel yazılımlar geliştiren OFSET adlı organizasyonun ikinci başkanıdır. Aynı zamanda bir dinamik geometri programı olan Dr.Geo'yu yazdı. Halen Gnome için bir başka eğitim programı olan Dr.Genius üzerinde çalışmaktadır.
Bu makale serisi özellikle Gnome ve GNU/Linux kullanan amatör programcılar için yazılmiştır. Geliştirme için seçilen dil, Python, C gibi derlenmiş dillerin alışageldiğimiz genel hatlarını kullanmaz. Bu makaleyi anlamanız ic cin temel Python programlama bilgisine sahip olmanız gerekir. Python ve Gnome hakkında ayrıntılı bilgiyi http://www.python.org ve http://www.gnome.org.
adreslerinden edinebilirsiniz.Serideki diğer makaleler :
- ilk makale
- ikinci makale
Bu makalede tanıtılan programı çalıştırmak için gereken yazılımlar için bu serinin 1.bölümüne bakınız.
Diğer gereksinimler:
Python-Gnome ve LibGlade kurulumu ve kullanımı için Bölüm 1'e bakınız.
Önceki makalede (Bölüm 2), ileride tanımlanacak alıştırmaların kodlaması için bir çerçeve olan -- Drill -- adlı kullanıcı arayüzünü oluşturduk. Şu aşamada, Drill'e yararlık kazandırmak açısından Python kullanarak nesne yönelimli geliştirmeyi daha yakından incelemeliyiz. Bu çalışmada, Gnome'da Python geliştirmeyi bir kenara bırakacağız.
O halde bıraktığımız yerden devam edelim, okuyucu için bir alıştırma olması bakımından Drill içine bir renk oyununun eklenmesine bakalım. Bunu konumuzu görselleştirmek, aynı zamanda da bu alıştırma için bir çözüm sunmak için yapacağız.
Kısaca, ayrıntılı bir çözümleme yapmaksızın, nesne yönelimli geliştirmenin olayların gerçekte olup olmadığını Bu budur ilişkileri ile tanımlamaya çalıştığını söyleyebiliriz. Bu ilgilendiğimiz problemdeki nesneleri özetlemek olarak da görülebilir.Değişik alanlarda karşılıklar bulabiliriz, Aristoteles'in kategorileri, taksonomiler, ontolojiler gibi. Bunların hepsinde, kişi karmaşık bir durumu özetleme yoluyla anlamak durumundadır. Bu tür bir geliştirme pekala kategori yönelimli geliştirme olarak adlandırılabilir.
Bu geliştirme modelinde, program tarafından değiştirilen yada programı oluşturan nesneler sınıflar bu özet nesnelerin temsilcileri de örnekler olarak anılır. Sınıflar nitelikler (değerler içeren) ve yöntemler (fonksiyonlar) ile tanımlanırlar. Aslında çocuk sınıfın özelliklerini ebeveyn sınıftan aldığıbir ebeveyn-çocuk ilişkisinden söz ediyoruz. Sınıflar bu budurilişkisiyle organize olmuşlardır ve bir çocuk hem bir aile tipidir hem de bir çocuk. Sınıflar tamamen tanımlanamayabilirler, böyle bir durumda özet sınıflar olarak adlandırılırlar. Bir yöntem ifade edilmiş fakat tanımlanmamışsa (fonksiyonun gövdesi boşsa) buna da sanal yöntem denir. Bir özet sınıf bu tanımlanmamış yöntemlerden bir veya birden fazla içerir ve bu nedenle örneklenemez. Özet sınıflar türetilmiş sınıfların - saf sanal yöntemlerin tanımlanacağı çocuk sınıflar - aldıkları formları tanımlamaya izin verir.
Çeşitli dillerin nesne tanımlamadaki zariflikleri farklıdır fakat ortak paydanın şunlar olduğunu söyleyebiliriz:
Python için bu seçilen en küçük ortak paydadır. Bu metodolojinin içinde kaybolmadan nesne yönelimli geliştirme öğrenmeye olanak sağlar.
Python'da, bir nesnenin yöntemleri her zaman sanal yöntemlerdir. Bu her zaman bir çocuk sınıf tarafından geçersiz kılınabileceği anlamına gelir - ki nesne yönelimli gelıştirmede biz bunu istiyoruz - ve söz dizimini biraz basitleştirir. Fakat bir yöntemin geçersiz kılınıp kılınmadığının ayırdına varmak kolay değildir. Bunun da ötesinde, bir nesneyi donuk kılmak, bunun sonucunda da nesne dışından niteliklerine ve yöntemlerine erişimi reddetmek olanaksızdır. Sonuç olarak, bir Python nesnesinin nitelikleri dışarıdan okunabilir ve yazılabilirdir.
Örneğimizde, (templateExercice.py dosyasına bakın), exercice tipinde nesneler tanımlamak istiyoruz. exercice tipi nesneyi tanımlayarak daha sonra oluşturacağımız exercice'ler için bir özet temel sınıf oluşturmuş olduk. exemple nesnesi oluşturulacak tüm diğer tip nesnelerin ebeveyn sınıfıdır. Bu türetilmiş tipler en azından exercice ile aynı nitelik ve yöntemlere sahiptirler. Bu bize her türden nesneye, nereden türetildiğine bakmaksızın, müdahale etme imkanı verecektir.
exercice sınıfına örnek olarak şunu yazabiliriz:
from templateExercice import exercice monExercice = exercice () monExercice.activate (ceWidget) |
Aslında exercice sınıfına örnek oluşturmak gereksizdir çünkü zaten diğer sınıfların türetildiği bir kalıptır.
Nitelikler
Eğer alıştırmanın başka yönleriyle de ilgileniyorsak, edinilen skoru yada kaç kez çalıştırıldığını belirten nitelikler ekleyebiliriz.
Yöntemler
Bu Python kodunda bize şu sonucu verir :
class exercice: "A template exercice" exerciceWidget = None exerciceName = "No Name" def __init__ (self): "Create the exericice widget" def activate (self, area): "Set the exercice on the area container" area.add (self.exerciceWidget) def unactivate (self, area): "Remove the exercice fromt the container" area.remove (self.exerciceWidget) def reset (self): "Reset the exercice" |
Bu kodu kendi dosyası olan templateFichier.py içermektedir, bu da bizim her nesnenin kendine özgü görevlerini ayırt etmemizi sağlar. Yöntemler, aslında fonksiyonlar demek daha doğru olur, exercice sınıfının içinde ifade edilmiştir.
area argümanının LibGlade tarafından oluşturulan GTK+ widget'ına referans olduğunu göreceğiz.
Bu yöntemde __init__ ve reset boştur ve gerektiğinde çocuk sınıflar tarafından geçersiz kılınacaktır.
Bu neredeyse boş bir alıştırma. Yalnızca bir tek şey yapıyor, o da Drill'in alıştırma bölgesine alıştırmanın adını koymak. Henüz oluşturmadığımız ama Drill'in sol tarafını dolduracak olan alıştırmalar için bir başlatıcı görevi görüyor.
exercice'da olduğu gibi, labelExercice da kendi dosyası olan labelExercice.py'a konuyor. Bundan sonra nesne exercice'in çocuğu için ebeveyninin nasıl tanımlandığını anlatmamız gerek. Bu basitçe import'la yapılıyor :
from templateExercice import exercice |
Bunun anlamı, exercice sınıfının templateExercice.py dosyasındaki tanımı bu koda getirtiliyor(import).
Şimdi en önemli noktaya geldik, labelExercice sınıfının exercice'in çocuğu olarak ifade edilmesi.
labelExercice aşağıdaki şekilde ifade edilir :
class labelExercice(exercice): |
İşte, bu labelExercice'in exercice.
'in tüm nitelik ve yöntemlerini alması için yeterli.Tabii ki, hala yapacak işimiz var, alıştırmanın widget'ını başlatmamız(initialize) gerekiyor. Bunu __init__ yöntemini geçersiz sayarak yapıyoruz. Bu widget'a exerciceWidget niteliğinde de atıfta bulunulmalıdır ki activate ve unactivate yöntemlerini geçersiz kılmamız gerekmesin.
def __init__ (self, name): self.exerciceName = "Un exercice vide" (Çevirmenin notu: boş bir alıştırma) self.exerciceWidget = GtkLabel (name) self.exerciceWidget.show () |
Bu geçersiz kıldığımız tek yöntem. labelExercice örneğini oluşturmak için yapmamiz gereken:
monExercice = labelExercice ("Un exercice qui ne fait rien") (Çevirmenin Notu: "Un exercice qui ne fait rien" "Hiçbir şey yapmayan alıstırma" demektir) |
Niteliklerine ve yöntemlerine erişmek için:
# Le nom de l'exercice (Çevirmenin notu: alıştırmanın adı) print monExercice.exerciceName # Placer le widget de l'exercice dans le container "area" # (Çevirmenin notu: Alıştırmanın widget'ını konteyner alanına yerleştir) monExerice.activate (area) |
Burada ilk makalede görülen renk oyununu adı colorExercice.py olan exercice tipi bir sınıfa dönüştürmeye başlıyoruz. Kendi dosyası olan colorExercice.py'ye koyuyoruz(kaynak kodu bu belgeye eklidir).
Baştaki kaynak kodda yapılması gereken değişiklikler çoğunlukla fonksiyonların ve değişkenlerin colorExercice sınıfındaki yöntem ve niteliklere yeniden dağıtımını içerir.
Küresel değişkenler sınıfın başında ifade edilerek niteliklere dönüştürülür:
class colorExercice(exercice): width, itemToSelect = 200, 8 selectedItem = rootGroup = None # to keep trace of the canvas item colorShape = [] |
labelExercice sınıfında olduğu gibi, __init__ yöntemi alıştırmanın widget'larının oluşturulması için geçersiz sayılmıştır:
def __init__ (self): self.exerciceName = "Le jeu de couleur" # Çevirmenin Notu: Renk Oyunu self.exerciceWidget = GnomeCanvas () self.rootGroup = self.exerciceWidget.root () self.buildGameArea () self.exerciceWidget.set_usize (self.width,self.width) self.exerciceWidget.set_scroll_region (0, 0, self.width, self.width) self.exerciceWidget.show () |
exerciceWidget'de atıfta bulunulan yalnız GnomeCanvas ise başlangıç koduna kıyasla yeni bir şey yok.
Geçersiz sayılan yöntem reset'dir. Oyunu sıfıra eşitlediği için renk oyununa uyarlanması gerekir:
def reset (self): for item in self.colorShape: item.destroy () del self.colorShape[0:] self.buildGameArea () |
Diğer yöntemler, eklenen değişken self dışında orjinal fonksiyonun doğrudan kopyasıdır. Yalnız buildStar ve buildShape yöntemlerinde istisnalar vardır, ondalık parametre k bir tam sayı ile değiştirilmiştir. colorExercice.py dökümanında garip bir davranışı fark ettim. Kaynak kod tarafından alınan ondalık sayılar kesiliyordu(truncation). Sorun gnome.ui modülünden Fransız notasyonundan kaynaklanıyor gibi görünüyor(ondalık kıs mı nokta yerine virgülle ayırırlar).Bir sonraki makaleye kadar sorunun kaynağını bulma üzerine çalışacağım.
Şimdi iki tip alıştırmamız var -- labelExercice ve colorExercice. drill1.py kodunda addXXXXExercice fonksiyonları ile örnekler oluşturabiliriz. exerciceList sözlüğünde bu örneklere atıfta bulunuluyor. Bu sözlükte anahtarlar aynı zamanda soldaki ağaçtaki her alıştırmanın sayfalarının argümanları:
def addExercice (category, title, id): item = GtkTreeItem (title) item.set_data ("id", id) category.append (item) item.show () item.connect ("select", selectTreeItem) item.connect ("deselect", deselectTreeItem) [...] def addGameExercice (): global exerciceList subtree = addSubtree ("Jeux") addExercice (subtree, "Couleur", "Games/Color") exerciceList ["Games/Color"] = colorExercice () |
addGameExercice fonksiyonu addExercice fonksiyonunu çağırarak ağaçta niteliği id="Games/Color" olan bir yaprak oluşturur. Bu nitelik colorExercice() tarafından exerciceList sözlüğünde oluşturulan renk oyunu örneğinde anahtar olarak kullanılır.
Polimorfluğun nesne yönelimli geliştirmedeki sıklığı dolayısıyla alıştırmalarıher nesne için farklı davranış gösteren aynı fonksiyonlarla yürütebiliriz. Yöntemleri yalnızca özet temel sınıfta exercice'da çağırıyoruz ve onlar colorExercice sınıfında ve labelExercice sınıfında farklı şeyler yapıyorlar. Programcı tüm alıştırmalara aynı şeyi "söylüyor", her alıştırmanın "yanıtı" biraz farklı olsa da. Bunu yapmak için ağacın yapraklarının id niteliği ile exerciceList sözlüğünün yada exoSelected değişkeninin kullanımını birleştiriyoruz. Bütün alıştırmaların exercice'ın çocuğu olduğu bilgisini kullanarak, onun yöntemlerini her türlü alıştırmayı kontrol etmek için kullanıyoruz.
def on_new_activate (obj): global exoSelected if exoSelected != None: exoSelected.reset () def selectTreeItem (item): global exoArea, exoSelected, exerciceList exoSelected = exerciceList [item.get_data ("id")] exoSelected.activate (exoArea) def deselectTreeItem (item): global exoArea, exerciceList exerciceList [item.get_data ("id")].unactivate (exoArea) |
Makalemiz burada bitiyor. Python'da nesne yönelimli geliştirmenin çekiciliğini grafik kullanıcı arayüzü ortamında keşfettik. İlerideki makalelerde Drill'e ekleyeceğimiz yeni alıştırmalar kodlayarak Gnome widget'larını keşfetmeye devam edeceğiz.
drill1.py
#!/usr/bin/python # Drill - Teo Serie # Copyright Hilaire Fernandes 2002 # Release under the terms of the GPL licence # You can get a copy of the license at http://www.gnu.org from gnome.ui import * from libglade import * # Import the exercice class from colorExercice import * from labelExercice import * exerciceTree = currentExercice = None # The exercice holder exoArea = None exoSelected = None exerciceList = {} def on_about_activate(obj): "display the about dialog" about = GladeXML ("drill.glade", "about").get_widget ("about") about.show () def on_new_activate (obj): global exoSelected if exoSelected != None: exoSelected.reset () def selectTreeItem (item): global exoArea, exoSelected, exerciceList exoSelected = exerciceList [item.get_data ("id")] exoSelected.activate (exoArea) def deselectTreeItem (item): global exoArea, exerciceList exerciceList [item.get_data ("id")].unactivate (exoArea) def addSubtree (name): global exerciceTree subTree = GtkTree () item = GtkTreeItem (name) exerciceTree.append (item) item.set_subtree (subTree) item.show () return subTree def addExercice (category, title, id): item = GtkTreeItem (title) item.set_data ("id", id) category.append (item) item.show () item.connect ("select", selectTreeItem) item.connect ("deselect", deselectTreeItem) def addMathExercice (): global exerciceList subtree = addSubtree ("Mathématiques") addExercice (subtree, "Exercice 1", "Math/Ex1") exerciceList ["Math/Ex1"] = labelExercice ("Exercice 1") addExercice (subtree, "Exercice 2", "Math. Ex2") exerciceList ["Math/Ex2"] = labelExercice ("Exercice 2") def addFrenchExercice (): global exerciceList subtree = addSubtree ("Français") addExercice (subtree, "Exercice 1", "French/Ex1") exerciceList ["French/Ex1"] = labelExercice ("Exercice 1") addExercice (subtree, "Exercice 2", "French/Ex2") exerciceList ["French/Ex2"] = labelExercice ("Exercice 2") def addHistoryExercice (): global exerciceList subtree = addSubtree ("Histoire") addExercice (subtree, "Exercice 1", "Histoiry/Ex1") exerciceList ["History/Ex1"] = labelExercice ("Exercice 1") addExercice (subtree, "Exercice 2", "Histoiry/Ex2") exerciceList ["History/Ex2"] = labelExercice ("Exercice 2") def addGeographyExercice (): global exerciceList subtree = addSubtree ("Géographie") addExercice (subtree, "Exercice 1", "Geography/Ex1") exerciceList ["Geography/Ex1"] = labelExercice ("Exercice 1") addExercice (subtree, "Exercice 2", "Geography/Ex2") exerciceList ["Geography/Ex2"] = labelExercice ("Exercice 2") def addGameExercice (): global exerciceList subtree = addSubtree ("Jeux") addExercice (subtree, "Couleur", "Games/Color") exerciceList ["Games/Color"] = colorExercice () def initDrill (): global exerciceTree, label, exoArea wTree = GladeXML ("drill.glade", "drillApp") dic = {"on_about_activate": on_about_activate, "on_exit_activate": mainquit, "on_new_activate": on_new_activate} wTree.signal_autoconnect (dic) exerciceTree = wTree.get_widget ("exerciceTree") # Temporary until we implement real exercice exoArea = wTree.get_widget ("exoArea") # Free the GladeXML tree wTree.destroy () # Add the exercice addMathExercice () addFrenchExercice () addHistoryExercice () addGeographyExercice () addGameExercice () initDrill () mainloop () |
templateExercice.py
# Exercice pure virtual class # exercice class methods should be override # when exercice class is derived class exercice: "A template exercice" exerciceWidget = None exerciceName = "No Name" def __init__ (self): "Create the exericice widget" def activate (self, area): "Set the exercice on the area container" area.add (self.exerciceWidget) def unactivate (self, area): "Remove the exercice fromt the container" area.remove (self.exerciceWidget) def reset (self): "Reset the exercice" |
labelExercice.py
# Dummy Exercice - Teo Serie # Copyright Hilaire Fernandes 2001 # Release under the terms of the GPL licence # You can get a copy of the license at http://www.gnu.org from gtk import * from templateExercice import exercice class labelExercice(exercice): "A dummy exercie, it just prints a label in the exercice area" def __init__ (self, name): self.exerciceName = "Un exercice vide" self.exerciceWidget = GtkLabel (name) self.exerciceWidget.show () |
colorExercice.py
# Color Exercice - Teo Serie # Copyright Hilaire Fernandes 2001 # Release under the terms of the GPL licence # You can get a copy of the license at http://www.gnu.org from math import cos, sin, pi from whrandom import randint from GDK import * from gnome.ui import * from templateExercice import exercice # Exercice 1 : color game class colorExercice(exercice): width, itemToSelect = 200, 8 selectedItem = rootGroup = None # to keep trace of the canvas item colorShape = [] def __init__ (self): self.exerciceName = "Le jeu de couleur" self.exerciceWidget = GnomeCanvas () self.rootGroup = self.exerciceWidget.root () self.buildGameArea () self.exerciceWidget.set_usize (self.width,self.width) self.exerciceWidget.set_scroll_region (0, 0, self.width, self.width) self.exerciceWidget.show () def reset (self): for item in self.colorShape: item.destroy () del self.colorShape[0:] self.buildGameArea () def shapeEvent (self, item, event): if event.type == ENTER_NOTIFY and self.selectedItem != item: item.set(outline_color = 'white') #highligh outline elif event.type == LEAVE_NOTIFY and self.selectedItem != item: item.set(outline_color = 'black') #unlight outline elif event.type == BUTTON_PRESS: if not self.selectedItem: item.set (outline_color = 'white') self.selectedItem = item elif item['fill_color_gdk'] == self.selectedItem['fill_color_gdk'] \ and item != self.selectedItem: item.destroy () self.selectedItem.destroy () self.colorShape.remove (item) self.colorShape.remove (self.selectedItem) self.selectedItem, self.itemToSelect = None, \ self.itemToSelect - 1 if self.itemToSelect == 0: self.buildGameArea () return 1 def buildShape (self,group, number, type, color): "build a shape of 'type' and 'color'" w = self.width / 4 x, y, r = (number % 4) * w + w / 2, (number / 4) * w + w / 2, w / 2 - 2 if type == 'circle': item = self.buildCircle (group, x, y, r, color) elif type == 'squarre': item = self.buildSquare (group, x, y, r, color) elif type == 'star': item = self.buildStar (group, x, y, r, 2, randint (3, 15), color) elif type == 'star2': item = self.buildStar (group, x, y, r, 3, randint (3, 15), color) item.connect ('event', self.shapeEvent) self.colorShape.append (item) def buildCircle (self,group, x, y, r, color): item = group.add ("ellipse", x1 = x - r, y1 = y - r, x2 = x + r, y2 = y + r, fill_color = color, outline_color = "black", width_units = 2.5) return item def buildSquare (self,group, x, y, a, color): item = group.add ("rect", x1 = x - a, y1 = y - a, x2 = x + a, y2 = y + a, fill_color = color, outline_color = "black", width_units = 2.5) return item def buildStar (self,group, x, y, r, k, n, color): "k: factor to get the internal radius" "n: number of branch" angleCenter = 2 * pi / n pts = [] for i in range (n): pts.append (x + r * cos (i * angleCenter)) pts.append (y + r * sin (i * angleCenter)) pts.append (x + r / k * cos (i * angleCenter + angleCenter / 2)) pts.append (y + r / k * sin (i * angleCenter + angleCenter / 2)) pts.append (pts[0]) pts.append (pts[1]) item = group.add ("polygon", points = pts, fill_color = color, outline_color = "black", width_units = 2.5) return item def getEmptyCell (self,l, n): "get the n-th non null element of l" length, i = len (l), 0 while i < length: if l[i] == 0: n = n - 1 if n < 0: return i i = i + 1 return i def buildGameArea (self): itemColor = ['red', 'yellow', 'green', 'brown', 'blue', 'magenta', 'darkgreen', 'bisque1'] itemShape = ['circle', 'squarre', 'star', 'star2'] emptyCell = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] self.itemToSelect, i, self.selectedItem = 8, 15, None for color in itemColor: # two items of same color n = 2 while n > 0: cellRandom = randint (0, i) cellNumber = self.getEmptyCell (emptyCell, cellRandom) emptyCell[cellNumber] = 1 self.buildShape (self.rootGroup, cellNumber, \ itemShape[randint (0, 3)], color) i, n = i - 1, n - 1 |