9. Liste Görünümü ve Eylem Çubuğu¶
Bu bölümde Liste Görünümünü, Açılır Kutu (DropDown) ve Eylem Çubuğu’nu anlatacağız. Konuları anlatırken daha önce geliştirdiğimiz uygulamalara eklentiler yaparak uygulamasını da göstereceğiz.
9.1. Liste Görünümü¶
Kivy’de listeler ve ilgili görünümler (adaptör, uyarlayıcı kullanarak), daha önce kullandığım GUI (GKA)’lardan biraz farklı
çalışıyor. Bu farklılık listelerin kullanımınız biraz zorşaltırmış gibi görünse de, bu ona listelerin daha esnek ve kullanışlı
olmasını sağlıyor. İlk olarak basit bir liste oluşturalım. Bu liste sadece verileri görüntülemek için kullanılacak, seçim
yapılamayacak ve herhangi bir eylem tanımlanamayacaktır. Bir liste oluşturmak için ListView nesnesini kullanırız.
ListView
nesnesinin en basit kullanımı paramtere olarak item_strings vermektir. Bu parametre Python
listesi helinde listelenecek olan elemanları alır. Programlamada dillerini listeleyecek bir programı
Liste 9.1‘deki gibi yazabiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.listview import ListView
class basitListeUyg(App):
def build(self):
duzen=BoxLayout()
programlama_dilleri=["Perl", "PHP", "Pure", "Python", "Rebol",
"Rexx", "Ruby", "Scheme", "Tcl"]
liste = ListView(item_strings=programlama_dilleri)
duzen.add_widget(Label(text="Programlama Dilleri"))
duzen.add_widget(liste)
return duzen
basitListeUyg().run()
|
Programı çalıştırdığımızda Şekil 9.1‘de görünen pencere açılacaktır.
9.1.1. SimpleListAdapter Adaptörü Kullanımı¶
Liste görünümlerini değiştirmek ve işerliğe kavuşturmak için ListView
nesnesine bir adaptör vermektir.
Bu adaptörlerden en basiti ise SimpleListAdapter nesnesidir. Bu adaptör sadece listelemek için kullanılır. Programlama dillerininin isismlerini
gösteren programı bu adaptörü kullanarak tekrar yazmak istersek: Liste 9.2‘deki gibi basitListe.py
dosyasını hazırlamamız gerekcek.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.adapters.simplelistadapter import SimpleListAdapter
from kivy.uix.listview import ListView
class basitListeUyg(App):
def build(self):
duzen=BoxLayout()
programlama_dilleri=["Perl", "PHP", "Pure", "Python", "Rebol",
"Rexx", "Ruby", "Scheme", "Tcl"]
basit_liste_adaptoru = SimpleListAdapter(data=programlama_dilleri, cls=Button)
liste = ListView(adapter=basit_liste_adaptoru)
duzen.add_widget(Label(text="Programlama Dilleri"))
duzen.add_widget(liste)
return duzen
basitListeUyg().run()
|
Şimdi bu programı açıklamaya çalışalım. Programda 14. satırdaki programlama_dilleri
listesi basit bir Python listesidir.
Görüntülemek istediğimiz elemanları bu listeye yazabiliriz. Liste yerine tüp (tuple) kullanabilirsiniz. Liste görünümünde
kullanabileceğimiz en basit adaptörün SimpleListAdapter
olduğunu söylemiştik. Bu adaptör sadece bir dizi elemanı
ekranda görüntülemek için kullanılabilir. Herhangi bir seçim ya da eylem gerçekleştiremezsiniz. Kullanımı oldukça basittir:
paramatere olarak data (listelenecek elemanlar) ve cls (görüntüleme biçimi) alır. Görüntüleme
biçimi basit olarak bir etiket (Label) ya da düğme (Button) olabilir. Biz yukarıda etiket (Label) kullandık.
İsterseniz aynı programı düpme (Button) ile tekrar çalıştırın. Program çalıştığında yine
Şekil 9.1‘de görünen pencere açılacaktır.
Şekil 9.2‘de resimde aynı programın, listedeki görüntüleme biçiminin (cls=Button
) düğme (Button) hali
görünmektedir.
Liste 9.2‘deki programı, c:\windows
(Linux için /usr
) klasöründeki dosya ve dizinleri
gösterecek şekilde uyarlayınız. Linux için programınızın çalışmış hali Şekil 9.3‘deki gibi olacaktır.
Çözüm:
basitListe-klasorler.py: https://github.com/mbaser/kivy-tr/blob/master/docs/programlar/listeEylem/programlar/1/basitListe-klasorler.py
Sonraki konuya geçmeden önce listelerin kv
dili ile nasıl hazırlanacağına bakalım. Liste 9.2‘deki programımızın
aynısını kv
dili ile yazalım. Önce Python programını Liste 9.3‘deki gibi yazalım
1 2 3 4 5 6 7 8 9 10 11 | # -*- coding: utf-8 -*-
from kivy.app import App
class basitListeUyg(App):
def build(self):
pass
basitListeUyg().run()
|
Sanırım bu program çok kolay oldu ve herhangi bir açıklamaya ihtiyacı yok. Bu program tarafından kullanılacak kv
dosyasını Liste 9.4‘de görüldüğü gibi yazabiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 | #:import Label kivy.uix.label.Label
#:import SimpleListAdapter kivy.adapters.simplelistadapter.SimpleListAdapter
<basitListeUyg>:
BoxLayout:
Label:
text: "Programlama Dilleri"
ListView:
id: listeci
adapter:
SimpleListAdapter(
data=["Perl", "PHP", "Pure", "Python", "Rebol", "Rexx", "Ruby", "Scheme", "Tcl"],
cls=Label)
|
Bu kv
dosyasının ilk iki stırında Label
ve SimpleListAdapter
nesnelerini ilgili modüllerden nasıl içerdiğimizi
iyice inceleyiniz. Program içerisinden adaptöre ve adaptörün verilerine ulaşabilir, güncelleyebilirsiniz ve hatta başka adaptör
kullanabilirsiniz. Aşağıdaki kodu build()
işlevinin altına yazarsanız, programlama dilleri listesine “Pascal”, “C” ve “C++” nin de
eklendiğini göreceksiniz:
for pr in ["Pascal", "C", "C++"]:
self.root.ids.listeci.adapter.data.append(pr)
9.1.2. ListAdapter Adaptörü Kullanımı¶
Listeler genellikle arasından birisni seçmek için kullanılır ve daha önce anlatılan basit liste görünümü oluşturmaktan
daha karmaşık veriye sahip olabilir. Bunları ListAdapter
veya DictAdapter
adaptörlerini kullanarak yapabiliriz.
Burada sadece ListAdapter
anlatılacaktır.
Önce ListAdapter kullanımına bakalım. Daha önce söyledğimiz gibi ListView
parçacığında adaptörler, veriyi
içerir. Bu veri ListAdapter
için her biri birer sözlük olan Python Listesidir. Örneğin kitaplara ait veriyi ele alalım
(not: kitaplar rastgele seçilmiştir, seçimde herhangi bir tercih yoktur):
kitaplar=[ {'adi':'Python', 'yazari':'Mustafa Başer', 'yayinevi':'Dikeyeksen'},
{'adi':'Ruby', 'yazari':'Timur Karaçay', 'yayinevi':'Seçkin'},
{'adi':'Perl-CGI', 'yazari':'Rıza Çelik', 'yayinevi':'Seçkin'},
{'adi':'Php', 'yazari':'Mehmet Şamlı', 'yayinevi':'Kodlab'} ]
Burada kitaplar
bildiğimiz bir Python listesi ve elemanları bildiğimiz Python sözlükleridir. Her sözlük
adi
, yazarı
ve yayinevi
anahtarları ile bunlara ait birer değer içermektedir. kitaplar
listesini
olduğu gibi Listadapter
nesnesine veri seti olarak atayabiliriz. Peki kullanıcıya ne gösterilecek? Bunu ise bir
işlev yadımı ile belirleyebiliriz. Bu işeve argüman çevirici (arg_converter) diyoruz. Argüman çevirici
kendisine gelen veriyi, listede gösterilecek şekilde düzenler ve yine bir sözlük döndürür. Bu sözlükte bulunması gereken
tek zorunlu anahtar text
dir. Bu anahtarın değeri kullanıcıya listede gösterilen metindir. İsterseniz gösterilecek olan liste
elemanı (burada ListItemButton olacaktır) (liste düğmesi) ile ilgili görünümü değiştirebilirsiniz; örneğin boyutunu.
Şimdi bunları birleştirelim ve seçilebilir bir liste oluşturalım. Programımızı Liste 9.5‘deki gibi yazalım
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.listview import ListView
from kivy.adapters.listadapter import ListAdapter
from kivy.uix.listview import ListItemButton
class kitaplarUyg(App):
def argumanCevirici(self, satir, nesne):
return {'text': nesne["adi"], 'size_hint_y': None, 'height': 25}
def build(self):
kitaplar=[ {'adi':'Python', 'yazari':'Mustafa Başer', 'yayinevi':'Dikeyeksen'},
{'adi':'Ruby', 'yazari':'Timur Karaçay', 'yayinevi':'Seçkin'},
{'adi':'Perl-CGI', 'yazari':'Rıza Çelik', 'yayinevi':'Seçkin'},
{'adi':'Php', 'yazari':'Mehmet Şamlı', 'yayinevi':'Kodlab'} ]
liste_adaptoru=ListAdapter(args_converter=self.argumanCevirici,
data=kitaplar,
cls=ListItemButton,
allow_empty_selection=False)
listeGorunumu=ListView(adapter=liste_adaptoru)
return listeGorunumu
kitaplarUyg().run()
|
Programımızı çalıştıracak olursak Şekil 9.4‘deki gibi bir pencere açılacaktır.
ListAdapter
nesnesi arg_converter
işlevine (burada argumanCevirici
) iki adet argüman gönderir. Bunlardan
ilki gönderilen verinin veri setindeki (data
parametresine atanan değer, burada kitaplar
listesi) konumu (indeksi)
ikincisi ise, verinin kendisi. kitaplar
listesinin her elemanı sıra ile bu işleve gönderilir. Bu işev yine bir
sözlük döndürür. Sözlük en az text
anahtarına sahip olmalıdır. text
anahtarının değeri kullanıcıya gösterilen metindir.
Liste 9.5‘daki programda size_hint_y
ve height
anhatarlarını kullanarak listedeki düğmenin
(ListItemButton
) boytunu 25 piksel yaptık. ListItemButton
nesnesi ile oluşturulan düğmelerin seçilmiş ve seçilmemiş
olanların rengini de değiştirebilirsiniz. Bunları deselected_color ve selected_color anahtarları ile yapabilirsiniz.
Örneğin Liste 9.5‘daki programda argumanCevirici()
işlevini aşağıdaki gibi yazarsanız, seçilmiş olan düğme rengi
mor, seçilmemiş olan düğmelerin rengi ise sarı olacaktır:
def argumanCevirici(self, satir, nesne):
return {'text': nesne["adi"],
'size_hint_y': None, 'height': 25,
'deselected_color': [1,1,0,1],
'selected_color': [1,0,1,1]
}
Eğer kullanıcıya görüntülenecek olan düğme üzerindeki metinde, sadece kitap adı yerine yazarının da görünmesini istiyorsanız,
argumanCevirici()
işlevini şu şekilde değiştirebilirsiniz:
def argumanCevirici(self, satir, nesne):
return {'text': '%s (%s)' % (nesne["adi"], nesne['yazari']),
'size_hint_y': None, 'height': 25,
'deselected_color': [1,1,0,1],
'selected_color': [1,0,1,1]
}
Programımızı bu işlev ile çalıştıracak olursak Şekil 9.5‘deki gibi bir pencere açılacaktır.
argumanCevirici()
işlevi sadece iki argüman alır ve bir sözlük dündürür. Bu tür bir işelev ihityacınız var ise bunu
Python’nun muhteşem lambda
ifadesi ile gerçekleştirebilirsiniz. lambda
ifadesi kendisine gelen argümanları birer
parametreye aktarır ve bu parametreleri kullanarak bir sonuç döndürme işlevi sağlar. Örneğin kendisine gelen sayıları
toplayan bir işleve ihtiyacımız var ise:
def topla(a,b):
return a+b
yerine:
>>> topla=lambda a,b: a+b
>>> topla(5,7)
12
şeklinde yazabiliriz. Bu bilgiler ışığında argumanCevirici()
işlevini hiç yazmadan arg_cvonverter
parametresine
aşağıdaki gibi lambda
ifadesini yazabiliriz:
liste_adaptoru=ListAdapter(args_converter=lambda satir, nesne: {'text':nesne["adi"],'size_hint_y': None, 'height': 25},
data=kitaplar,
cls=ListItemButton,
allow_empty_selection=False)
ListAdapter
parçacığında anlatmadığımız allow_empty_selection paramteresine False
değerini verirseniz,
listede mutlaka bir seçim yapılmış olması gerekir. Bu durumda seçim yapılmamış ise, veri setinin ilk elemanı otomatik
olarak seçili hale gelir. Eğer allow_empty_selection
değerini True
yaparsanız (ön tanımlı değeri budur), listede bir
seçim yapma zorunluluğu olmaz.
Liste 9.5‘daki programı kv
dili ile yazalım. Önce ana programı Liste 9.6‘daki gibi yazalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # -*- coding: utf-8 -*-
from kivy.app import App
class kitaplarUyg(App):
def argumanCevirici(self, satir, nesne):
return {'text': nesne["adi"], 'size_hint_y': None, 'height': 25}
def build(self):
kitaplar=[ {'adi':'Python', 'yazari':'Mustafa Başer', 'yayinevi':'Dikeyeksen'},
{'adi':'Ruby', 'yazari':'Timur Karaçay', 'yayinevi':'Seçkin'},
{'adi':'Perl-CGI', 'yazari':'Rıza Çelik', 'yayinevi':'Seçkin'},
{'adi':'Php', 'yazari':'Mehmet Şamlı', 'yayinevi':'Kodlab'} ]
self.root.ids.kitaplar.adapter.data=kitaplar
kitaplarUyg().run()
|
Sanırım bu programda herşey açık. kv
dosyasını ise Liste 9.7‘daki gibi hazırladım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #:import ListItemButton kivy.uix.listview.ListItemButton
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
<kitaplarUyg>:
BoxLayout:
orientation: 'vertical'
Label:
text: "Kitap Seçiniz"
size_hint_y: None
height: 30
canvas.before:
Color:
rgba: [0, 1, 0, 1]
Rectangle:
pos: self.pos
size: self.size
ListView:
id: kitaplar
adapter:
ListAdapter(args_converter=app.argumanCevirici,
data=[],
cls=ListItemButton,
allow_empty_selection=False)
|
Bu dosyayı inceleyecek olursanız, etikette ilk defa (11. satırda) canvas.before
parametresini kullanmış olduk.
Ne yazıkki Kivy geliştiricileri bir etiketin arka plan rengini değiştirebilmemiz için bir parametre koymamış. Bunun
yerine parçacık çizilmeden önce tuvalin (canvas) arka planını boyamamız gerekir. Bunu da şu satırlar ile yapıyoruz:
canvas.before:
Color:
rgba: [0, 1, 0, 1]
Rectangle:
pos: self.pos
size: self.size
9.1.3. Seçimin Denetlenmesi¶
En azından ListAdapter
kullanılan liste görünümlerinde bir seçim yapıldığında, bir eylem gerçekleştirilmek isteniyorsa
adaptörün on_selection_change olayına bir işlev bağlamak gerekir. Liste 9.6‘daki programda seçilen
seçilen kitaba ait ayrıntıları açılır pencerede görüntülemek üzere build()
işlevinin en altına şu satırı ekleyelim:
self.root.ids.kitaplar.adapter.bind(on_selection_change=self.secim)
Bir kitap seçildiğinde secim()
işlevi çağrılacaktır. Bu işleve, adaptörün kendisi argüman olarak verilecektir.
Bir adaptörde seçilen maddeler ait ListItemButton
nesneleri Python listesi halinde selection özelliğinden alınabilir (unutmayın birden fazla seçime olanak
sağlamak için adatörün
selection_mode parametresinin değerini multiple
yapmalısınız). Seçilen maddelerin listedeki konumlarını ise
ListItemButton
nesnesinin index
özelliği ile alabiliriz. O halde secim
işlevini şu şekilde yazabiliriz:
def secim(self, nesne):
if nesne.selection:
secimID=nesne.selection[0].index
secilenKitap=nesne.data[secimID]
icerik=Label(text='Kitap Adı: %s\nYazarı: %s\nYayınevi: %s' % (
secilenKitap['adi'],
secilenKitap['yazari'],
secilenKitap['yayinevi']))
popup = Popup(title='Seçilen Kitap',
content=icerik,
size_hint=(None, None), size=(200, 150))
icerik.bind(on_touch_down=popup.dismiss)
popup.open()
Aşağıdaki satırları programınızın başına yazmayı unutmayın:
from kivy.uix.popup import Popup
from kivy.uix.label import Label
Şu halde programımızı çalıştırıp bir kitap seçtiğimizde, bir popup açılacak ve burada kitap detayları görüntülecektir (Şekil 9.6).
9.1.4. İl-İlçe-Mahalle Seçimi¶
Liste görünümü ile ilgili daha ayrıntılı bir örnek yapalım. Hemen birçok alışveriş sitesinde bulunduğunuz il ve ilçe seçimi ile ilgili açılır listeler bulunur. Biz buna bir de mahalleyi ekleyeceğiz. Uygulamamız şöyle tasarlayalım:
- Ana pencereyi dikey olarak üç parçaya bölelim. Bunun için 3 sütunlu bir ızgara pencere düzenine ihtiyacımız olacak.
- Birinci sütunda illeri gösterelim. Buranın illeri gösterdiğini kullanıcıya bildirmek için etiketten bir başlık oluşturalım ve metnine “İl Seçiniz” yazalım. Etiketin altında liste görünümünde tüm illeri sıralayalım
- İkinci sütunda ilçeleri gösterelim. Buranın ilçeleri gösterdiğini kullanıcıya bildirmek için etiketten bir başlık oluşturalım ve metnine “İlçe Seçiniz” yazalım. Birinci sütunda seçilen ile ait ilçeleri etiketin altında liste görünümünde sıralayalım.
- Üçüncü sütunda semtleri gösterelim. Buranın semtleri gösterdiğini kullanıcıya bildirmek için etiketten bir başlık oluşturalım ve metnine “Semt Seçiniz” yazalım. İkinci sütunda seçilen ilçeye ait semtleri etiketin altında liste görünümünde sıralayalım.
Uygulamamazı yazmadan önce ülkemizin illeri, bu illerin ilçeleri ve her ilçenin semtlerinin listesi gerekli. Bunu teker teker elinizle yazabilirsiniz. Ya benim gibi da internette “İl-ilçe-Semt-Mahalle-PostaKodu” şeklinde aratırsanız, muhtemelen birçok veritabanı bulacaksınız. Aramam sonucunda şu adreste:
http://daltinkurt.com/Yazi/227/Il-Ilce-Semt-Mahalle-Veritabanlari-ve-Uygulamasi.aspx
bir tane buldum. Orada birçok veritabanı biçiminde hazırlanmış biçimlerini mevcut. Ben xml biçimini kullanacağım. Yerel kopyasını buradan edinebilirsiniz (bunun için Sn. Devrim Altınkurt’dan izin alınmıştır):
Il-ilce-Semt-Mahalle-PostaKodu.xml: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/3/Il-ilce-Semt-Mahalle-PostaKodu.xml
Il-ilce-Semt-Mahalle-PostaKodu.xml
dosyası oldukça büyük (~10 MB). Bu kadar büyük bir veriyi xml ile saklamak ve sonra onu
okuyup ayırt etmek oldukça zaman alıcı bir işlem, bu kadar büyük bir veriyi en azında SqLite veritabanı sisteminde saklamak daha iyi.
Ancak biz sadece deneme programı yazacağız, hem böylece bir xml dosyasını nasıl okuyacağınızı da öğrenmiş oluruz.
Bir xml dosyasını kullanabilmek için, öncelikle dosya yapısını bilmeniz gerekir. Onun için dosyayı firefox ile açıp yapısını inceleyebiliriz (Metin düzenleyici ya da xml editörleri ile açmayı denemeyin, dosya çok büyük). Dosyayı incelediğimzde veri gruplandırmasının yapılmadığını görüyoruz. Dosyanın yapısından anladığımız kadarı ile aşağıdaki çıkarımları yapabiliriz:
- İller
<tbl_il>
etiketlerinde iller mevcut. Her il için bir ID verilmiş (<il_id>
), il isimleri ise<il_ad>
etiketi ile beirtilmiş.- İlçeler
- İlçelerimiz
<tbl_ilce>
etiketleri ile ayrılmış. Her ilçe için bir ID verilmiş (<ilce_id>
). İlçenin hangi ile ait olduğu ise<il_id>
etiketi ile belirtilmiş. İlçe adı ise<ilce_ad>
etiketinde. - Semtler
- Her ilçenin semtleri
<tbl_semt>
etiketleri ile verilmiş. Bağlı olduğu ilçe<ilce_id>
, semt adı ise<semt_ad>
etiketi ile verilmiş. Elbetteki ID’si mevcut ve<semt_id>
etiketinin metninde bulunuyor.
xml Dosyasının yapısını öğrendiğimize göre bu dosyayı okuyup veriyi bizim için ayır edecek Python modülünü kullanmamız gerekir. Önce Python konsolunda bunu öğrenelim. Dosyayı bir klasöre indirin ve Python konsolundan aşağıdaki satırşarı işletin:
>>> from xml.etree import ElementTree
>>> agac = ElementTree.parse('/home/mbaser/Downloads/Il-ilce-Semt-Mahalle-PostaKodu.xml')
>>> xmlKok = agac.getroot()
Burada ikinci satırda dosya patikasını kullandığınız işletim sistemine göre doğru şekilde belirtiniz. Dosya çok büyük olduğu için okunması donanımınızın durumuna göre birkaç saniye alabilir. Şimdi illerimizi bulalım:
>>> iller=xmlKok.findall('tbl_il')
Şimdi iller
listesinde tüm illerimizi ait xml elemanları bulunacaktır. Bunları ekrana bastırmak için:
>>> for il in iller:
... print il.find('il_id').text, il.find('il_ad').text.encode("utf-8")
...
1 Adana
2 Adıyaman
3 Afyonkarahisar
4 Ağrı
5 Amasya
6 Ankara
7 Antalya
8 Artvin
9 Aydın
10 Balıkesir
.
.
.
Burada il adının UTF-8 biçimli bir metin olduğunu biliyoruz. Python 2.x’de düzgün gösterilebilmesi için metnin encode("utf-8")
özelliğini kullanmamız gerekiyor. Bir döngüyü kullanarak istediğimiz şekilde liste oluşturmayı daha önce anlatmıştık tekrarlayalım:
>>> sayi_listesi=[ "Sayi-%d" % x for x in range(1,11)]
>>> sayi_listesi
['Sayi-1', 'Sayi-2', 'Sayi-3', 'Sayi-4', 'Sayi-5', 'Sayi-6', 'Sayi-7', 'Sayi-8', 'Sayi-9', 'Sayi-10']
Bir liste içerisinde yazdığımız döngünün nasıl bir sonuç doğurduğunu gördük. Aynı listeyi şöyle de oluşturabilirdiniz:
>>> sayi_listesi=[]
>>> for x in range(1,11):
... sayi_listesi.append("Sayi-%d" % x)
...
>>> sayi_listesi
['Sayi-1', 'Sayi-2', 'Sayi-3', 'Sayi-4', 'Sayi-5', 'Sayi-6', 'Sayi-7', 'Sayi-8', 'Sayi-9', 'Sayi-10']
İlk yaptığımız daha kolay değil mi? Peki iller
xml elemanlarını kullanarak, liste adaptörümüz için nasıl bir veri listesi oluşturacağız?
Bu soruyu şöyle cevaplayalım:
>>> iller_veri = [
{'ilId': il.find('il_id').text, 'adi': il.find('il_ad').text.encode("utf-8")}
for il in iller
]
Liste içindeki döngülerde if
ifadesini de kullanabiliriz. Örneğin il_id
si 10’dan küçük illerin listesini elde edelim:
>>> iller_veri = [
{'ilId': il.find('il_id').text, 'adi': il.find('il_ad').text.encode("utf-8")}
for il in iller if int(il.find('il_id').text) < 10
]
Bunları öğrendiğimize göre, liste görünümü bilgilerini de kullanarak programımızı yazabiliriz. Önce ana programı Liste 9.8‘daki gibi yazalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | # -*- coding: utf-8 -*-
from kivy.app import App
from xml.etree import ElementTree
agac = ElementTree.parse('Il-ilce-Semt-Mahalle-PostaKodu.xml')
xmlKok = agac.getroot()
class illerIlceler(App):
def argCevir(self, satir, nesne):
return {'text': nesne["adi"], 'size_hint_y': None, 'height': 25}
def ilSecim(self, nesne):
if nesne.selection:
secim=nesne.selection[0].index
ilId=nesne.data[secim]['ilId']
ilceler=xmlKok.findall('tbl_ilce')
self.root.ids.ilceler.adapter.data= [
{'ilceId': ilce.find('ilce_id').text, 'adi': ilce.find('ilce_ad').text.encode("utf-8")}
for ilce in ilceler if ilce.find('il_id').text==ilId
]
self.root.ids.ilceler._trigger_reset_populate()
def ilceSecim(self, nesne):
if nesne.selection:
secim=nesne.selection[0].index
ilceId=nesne.data[secim]['ilceId']
semtler=xmlKok.findall('tbl_semt')
self.root.ids.semtler.adapter.data= [
{'semtId': semt.find('semt_id').text, 'adi': semt.find('semt_ad').text.encode("utf-8")}
for semt in semtler if semt.find('ilce_id').text==ilceId
]
self.root.ids.semtler._trigger_reset_populate()
def build(self):
iller=xmlKok.findall('tbl_il')
self.root.ids.iller.adapter.data= [
{'ilId': il.find('il_id').text, 'adi': il.find('il_ad').text.encode("utf-8")}
for il in iller
]
self.root.ids.iller.adapter.bind(on_selection_change=self.ilSecim)
self.root.ids.ilceler.adapter.bind(on_selection_change=self.ilceSecim)
illerIlceler().run()
|
Sanırım burada bilmediğimiz tek şey bir liste görünümünün _trigger_reset_populate()
özelliği. Bir liste görnümünün elemanlarını
değiştirdiğinizde, bunların ekrana yansıması için bu özelliği kullanmamız gerekiyor. İnternet tarayıcıda değişen sayfayı yenilemek
için “F5” tuşuna basmak gibi.
kv
dosyasını ise Liste 9.9‘daki gibi hazırladım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | #:import ListItemButton kivy.uix.listview.ListItemButton
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
<illerIlcelerUyg>:
GridLayout:
cols: 3
BoxLayout:
orientation: 'vertical'
Label:
text: "İl Seçiniz"
size_hint_y: None
height: 30
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
ListView:
id: iller
adapter:
ListAdapter(args_converter=app.argCevir,
data=[],
cls=ListItemButton,
allow_empty_selection=False)
BoxLayout:
orientation: 'vertical'
Label:
text: "İlçe Seçiniz"
size_hint_y: None
height: 30
canvas.before:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
ListView:
id: ilceler
adapter:
ListAdapter(args_converter=app.argCevir,
data=[],
cls=ListItemButton,
allow_empty_selection=False)
BoxLayout:
orientation: 'vertical'
Label:
text: "Semt Seçiniz"
size_hint_y: None
height: 30
canvas.before:
Color:
rgba: 0, 0, 1, 1
Rectangle:
pos: self.pos
size: self.size
ListView:
id: semtler
adapter:
ListAdapter(args_converter=app.argCevir,
data=[],
cls=ListItemButton,
allow_empty_selection=False)
|
Bu programda her liste görnümü için yeni bir argüman çevirici yazmak yerine sadece bir tane yazdık. Bunun için tüm liste adaptarlerinin,
veri setini hazırlarken görüntülenecek metni (il, ilçe, semt) adlarını adi
anahtarına koyduk. Böylece liste gornümü ne olursa olsun,
listede görüntülenecek metin adi
anahtarında bulunduğundan sadece bir tane argCevir()
işlevi yazmak yeterli oldu.
Programı çalıştıracak olursanız Şekil 9.7 de görünen pencere açılacaktır.
Not
Derleme yaparken (Paket Derleme kısmında anlatılan) buildozer in paketin içine Il-ilce-Semt-Mahalle-PostaKodu.xml
dosyasını koyması için source.include_exts
seçeneğinin sonuna xml eklenemsi gerekir
(tüm xml uzantılı dosyalar içerilecektir):
source.include_exts = py,png,jpg,kv,atlas,xml
Benim anlatacaklarım bitti. Şimdi kod yazma sırası size. xml dosyasında her semte ait mahalelerde mevcut. Biz bunları göstermedik. Programa mahalle seçimini de yapacağımız eklentileri yapın. Programınız çalıştığında Şekil 9.8 deki gibi görünmeldir.
Çözüm:
main-mahalleler.py: https://github.com/mbaser/kivy-tr/blob/master/docs/programlar/listeEylem/programlar/3/main-mahalleler.py illerilcelermahaller.kv: https://github.com/mbaser/kivy-tr/blob/master/docs/programlar/listeEylem/programlar/3/illerilcelermahaller.kv
9.2. Açılır Kutu¶
Liste görünümü ile açılır kutu (DropDown) benzer işler yapsa da, temel işleyiş mantığı olarak birbirinden farklıdır. Liste görünümünde, verdiğimiz seçenekler (ister seçilebilir düğme, iseterse etiket üzerinde olsun) kullanıcıya doğrudan gösterilir. Açılır kutularda ise, kullanıcı kutuya tıkladıktan sonra seçenekler ortaya çıkar. Önce nasıl çalıştığını öğrenelim, daha sonra yukarıdaki örneği açılır kutu ile tekrar yapalım.
Daha önce de belirttiğim gibi Kivy’de birçok şey, bildiğimiz masaüstü GUI (GKA)’lerden farklı bir yapıya sahip. Bir açılır kutuyu oluşturmak
için daha fazla emek harcamak gerekiyor. Örneğin bir web sayfasında açılır kutu oluşturmak için <select>
etiketi arasına seçenekleri
(<option>
) ard arda yazdıkmı işimiz tamamlanıyor. Ancak Kivy’de bu kadar kolay değil. Bir açılır kutu oluşturduğunuzda bunu
pendere düzeninde öyle istediğiniz yere koyamazsınız. Bu açılır kutuyu göstermek için bir başka nesneye ihtiyacınız olacak.
Örneğin bir düğme. Bu düğmeye tıklayıp bıraktığınızda (on_release olayı), açılır kutuyu görünür yapabilirsiniz. Aslında herhangi
bir nesnenin herhangi bir olayına da bağlayabilirsiniz. Biz bir düğmeye tıklanıp bırakıldığında açılır kutuyu göstereceğiz.
Açılır kutu nesnesi DropDown()
ile oluşturulur. Açılır kutunun open()
özelliği ile görünür hale getirilince, kendisine eklenmiş olan diğer nesneleri gösterecektir. Bu nesnelerden
herhangi birisi bildiğiniz düğme (Button
) ya da etiket (Label
) olabilir. Bunları toparlayacak olursak birkaç isimden oluşan bir açılır
kutuyu şu şekilde oluşturabiliriz:
acilirkutu = DropDown()
for isim in ( "Mustafa", "Dilek", "Fatih", "Melike"):
dugme=Button(text=isim, size_hint_y=None, height=25)
acilirkutu.add_widget(dugme)
Gördüğünüz gibi, yaptığımız iş DropDown()
nesnesine bildik düğmeleri ekledik. Peki bir düğme seçilince ne olacak? Programımızı yazarken
bunu da eklemek durumundayız. Açılır kutuyu oluşturduk ancak, bu açılır kutunun görünebilmesi için herhangi bir eyleme bağlamamız gerekir.
Bir düğme oluşturalım (anadügme
) ve bu düğmenin on_release
eylemine açılır kutunun open()
özelliğini bağlayalım.
Bu bilgiler ışığında Liste 9.10‘daki gibi bir program yazalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
class acilirKutu(App):
def secim(self, nesne):
self.acilirkutu.select(nesne.text)
self.anadugme.text=nesne.text
def build(self):
duzen = BoxLayout()
self.anadugme = Button(text='Başlat', size_hint=(None, None))
self.acilirkutu = DropDown()
for x in ( "Mustafa", "Dilek", "Fatih", "Melike"):
dugme=Button(text=x, size_hint_y=None, height=25)
dugme.bind(on_release=self.secim)
self.acilirkutu.add_widget(dugme)
self.anadugme.bind(on_release=self.acilirkutu.open)
duzen.add_widget(self.anadugme)
return duzen
acilirKutu().run()
|
Bu programda gördüğünüz gibi, açılır kutuya eklediğimiz her düğmenin on_release
eylemine (tıklanıp bırkalma) secim()
işlevini bağladık.
Diğer bir deyişle açılır kutudaki düğmelerden herhangi biri seçildiğinde secim()
işlevi çağrılacaktır. Bu işlev çağrıldığında
açılır kutunun ilgili düğmesi seçilmiş olacak (acilirkutu.select(nesne.text)
ile) ve anadugme
nin üzerindeki metin, seçilmiş
olan düğmenin üzerindeki metin olacaktır. Programınız çalıştığında Şekil 9.9 deki gibi görünmelidir.
Aynı programı kv
dili ile yazacak olursak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.lang import Builder
kv='''
BoxLayout:
Button:
id: anadugme
text: "Başlat"
on_release: app.acilirkutu.open(self)
size_hint: (None, None)
'''
class acilirKutuKv(App):
def secim(self, nesne):
self.acilirkutu.select(nesne.text)
self.root.ids.anadugme.text=nesne.text
def build(self):
self.root=Builder.load_string(kv)
self.acilirkutu = DropDown()
for x in ( "Mustafa", "Dilek", "Fatih", "Melike"):
dugme=Button(text=x, size_hint_y=None, height=25)
dugme.bind(on_release=self.secim)
self.acilirkutu.add_widget(dugme)
return self.root
acilirKutuKv().run()
|
Herhangi bir düğmeye herhangi bir özellik ekleyerek başka verileri saklayabiliriz. Örneğin öğrencilerin okul numaralarını saklamak için şu kodu yazabiliriz:
for x, y in (("Mustafa", 9876), ("Dilek", 77192),
("Fatih", 98278), ("Melike", 56765)):
dugme=Button(text=x, size_hint_y=None, height=25)
dugme.okulNo=y
dugme.bind(on_release=self.secim)
self.acilirkutu.add_widget(dugme)
Sakladığımız bu veriyi seçim yaptıktan sonra erişebiliriz. “Başlat” düğmesinde saçim yapıldıktan sonra hem isim hemde okul numarasını görüntülemek istiyorsak:
self.anadugme.text="Adı: %s\nNo: %d" % (nesne.text, nesne.okulNo)
9.2.1. Seçimin Denetlenmesi¶
Peki seçilmiş maddeyi nasıl belirleyeceğiz. Diğer bir deyişle tekrar seçim yapılacağı zaman daha önce seçilmiş olan düğmeyi nasıl
göstereceğiz? Liste görünümü (ListView) bunu kendisi yapıyordu, açılır kutuda bunu kendimiz yapmamız gerekecek. Bir seçim yapıldığında,
seçilen nesnenin (burada bildiğimiz düğme - Button) arka plan rengini değiştirebiliriz. Bunu hemen secim()
işlevinin altına şu
satırı yazarak gerçekleştirebiliriz:
nesne.background_color= 1, 0, 0, 1
secim()
işlevinin altına bu satırı ekledikten sonra programı çalıştırırsanız, bir seçim yapıp tekrar seçim yapmak istediğinizde
önceki düğmenin arka plan renginin kırmızı olduğunu göreceksiniz. İşmiz bitti mi? Bu cevabı iknci ve üçncü seçimi yaptıktan sonra
siz verin. Ne oldu? Her seçilenin arkası kırmızıya dönüşüyor değil mi? Şimdi çözmemiz gereken başka bir sorun var. Daha önce seçilmiş
düğmenin arka plan rengini eski haline getirmek. Düğmelerin ön tanımlı arka plan rengi [1, 1, 1, 1]
dir. Bunu yapmak için iki
yolumuz var, ya açılır kutudaki tüm düğmelerin arka plan rengini önce [1, 1, 1, 1]
yapmak daha sonra, yeni seçilenin
arka plan rengini [1, 0, 0, 1]
yapmak. Ya da benim daha çok sevdiğim ikinci yöntem: açılır kutuya secim
özelliği verip,
her seçim yapıldığında seçilen nesneyi buraya atamak. Böylelikle daha sonra bir açılır kutudaki seçilmiş nesneye ait bilgilere de
ulaşabilirsiniz (aslında çok basit ikinci yöntemi Kivy geliştiricileri neden eklemezler bilmiyorum). Bu ikinci yöntemi uygulamak
için açılır kutuyu tanımladığımız satırdan sonra (Liste 9.10‘da 19. satıra) aşağıdaki satırı eklemeliyiz:
self.acilirkutu.secim=None
Bu durumda secim()
işlevini aşağıdaki gibi değiştirmemiz gerekecek:
def secim(self, nesne):
# Önceden seçilmiş bir düğme var ise arka plan rengini
# ön tanımlı renge dönüştürelim
if self.acilirkutu.secim:
self.acilirkutu.secim.background_color= [1, 1, 1, 1]
self.acilirkutu.select(nesne.text)
self.anadugme.text=nesne.text
# secim özelliğine yeni nesneyi ekleyelim ve
# arka plan rengini kırmızı yapalım
self.acilirkutu.secim=nesne
nesne.background_color= 1, 0, 0, 1
Üçüncü çözümü olan var mı?
9.2.2. İller-İlçeler-Semtler: SqLite Veritabanı¶
İl-ilçe-semt seçimini İl-İlçe-Mahalle Seçimi kesiminde xml ile yapmıştık ve orada bu kadar büyük veriyi xml ile saklamanın çok mantıklı olmadığını belirtmiştik. Burada aynı uygulamayı liste görünümü yerine açılır kutu ile yapacağız ve veritabanı olarak xml yerine SqLite kullanacağız.
SqLite oldukça hızlı, küçük (hafif), büyük veriler ile rahatlıkla başedebilen ve yaygın bir veritabanı sistemidir. Üstelik veri bir dosyada
tutulduğundan değişik diller ile yazılmış programlar rahatlıkla erişebilir ve ağ üzerinden dağıtılabilir. Python ile SqLite bağlantısı
sqlite3
modülü ile gerçekleştirilir. Önce basitçe kullanımını hatırlayalım. Bu amaçla bir veritabanı oluşturalım ve bağlantısını
gerçekleştirelim:
>>> import sqlite3
>>> sqlbaglantisi = sqlite3.connect('ogrenciler.db')
>>> isaretci = sqlbaglantisi.cursor()
ogrenciler
tablosunu oluşturalım:
>>> isaretci.execute('create table ogrenciler (ogr_id integer primary key, ogr_adi varchar(100), ogr_no integer)')
tabloya birkaç veri ekleyelim:
>>> for x, y in (("Mustafa", 9876), ("Dilek", 77192), ("Fatih", 98278), ("Melike", 56765)):
... isaretci.execute('insert into ogrenciler (ogr_adi, ogr_no) values ("%s", %d)' % (x,y))
...
Eklediğimiz verinin diske (dosyay) yazılmasını sağlayalım:
>>> sqlbaglantisi.commit()
Şimdi de eklediğimiz öğrencileri tekrar alalım:
>>> isaretci.execute("select * from ogrenciler")
>>> ogrenciler.fetchall()
>>> for ogr in ogrenciler:
... print ogr
...
(1, u'Mustafa', 9876)
(2, u'Dilek', 77192)
(3, u'Fatih', 98278)
(4, u'Melike', 56765)
Veritabanına yapılmış bağlantıyı koparalım:
>>> sqlbaglantisi.close()
Daha önce size verdiğim Il-ilce-Semt-Mahalle-PostaKodu.xml
dosyasındaki tüm tablo ve verileri SqLite biçimine dönüştürdüm.
SqLite dosyasını şu adresten alabilirziniz:
Dosyayı indirdiğiniz patikanın tam adını veritabanı dosyasının patikasını yazarak deneyelim:
>>> import sqlite3
>>> sqlbaglantisi = sqlite3.connect('/home/mbaser/Downloads/iller.db')
>>> isaretci = sqlbaglantisi.cursor()
>>> isaretci.execute("select * from tbl_il")
<sqlite3.Cursor object at 0x7f3c088ebc70>
>>> iller=isaretci.fetchall()
>>> for il in iller:
... print il[0], il[1]
...
1 Adana
2 Adıyaman
3 Afyonkarahisar
4 Ağrı
5 Amasya
.
.
.
Veritabanımız hazır olduğuna göre programımızı yazabiliriz (Liste 9.11).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | # -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
import sqlite3
sqlbaglantisi = sqlite3.connect('iller.db')
isaretci = sqlbaglantisi.cursor()
class illerIlcelerAcilirKutu(App):
def secimIl(self, nesne):
if self.iller.secim:
self.iller.secim.background_color=1, 1, 1, 1
self.iller.secim=nesne
self.iller.select(nesne.text)
self.root.ids.iller.text="Seçilen İl: %s" % nesne.text
nesne.background_color= 1, 0, 0, 1
self.ilceler.clear_widgets()
self.semtler.clear_widgets()
self.root.ids.ilceler.text="İlçe Seçiniz"
self.root.ids.semtler.text="Semt Seçiniz"
isaretci.execute('SELECT ilce_id, ilce_ad FROM tbl_ilce WHERE il_id=%d' % nesne.il_id)
for ilce in isaretci.fetchall():
dugme=Button(text=ilce[1].encode("utf-8"), size_hint_y=None, height=25)
dugme.ilce_id=ilce[0]
dugme.bind(on_release=self.secimIlce)
self.ilceler.add_widget(dugme)
def secimIlce(self, nesne):
if self.ilceler.secim:
self.ilceler.secim.background_color=1, 1, 1, 1
self.ilceler.secim=nesne
self.ilceler.select(nesne)
self.root.ids.ilceler.text="Seçilen İlçe: %s" % nesne.text
nesne.background_color= 1, 0, 0, 1
self.semtler.clear_widgets()
self.root.ids.semtler.text="Semt Seçiniz"
isaretci.execute('SELECT semt_id, semt_ad FROM tbl_semt WHERE ilce_id=%d' % nesne.ilce_id)
for semt in isaretci.fetchall():
dugme=Button(text=semt[1].encode("utf-8"), size_hint_y=None, height=25)
dugme.semt_id=semt[0]
dugme.bind(on_release=self.secimSemt)
self.semtler.add_widget(dugme)
def secimSemt(self, nesne):
if self.semtler.secim:
self.semtler.secim.background_color=1, 1, 1, 1
self.semtler.secim=nesne
self.semtler.select(nesne)
self.root.ids.semtler.text="Seçilen Semt: %s" % nesne.text
nesne.background_color= 1, 0, 0, 1
def build(self):
self.iller = DropDown()
self.iller.secim=None
isaretci.execute('SELECT il_id, il_ad FROM tbl_il')
for il in isaretci.fetchall():
dugme=Button(text=il[1].encode("utf-8"), size_hint_y=None, height=25)
dugme.il_id=il[0]
dugme.bind(on_release=self.secimIl)
self.iller.add_widget(dugme)
self.ilceler = DropDown()
self.ilceler.secim=None
self.semtler = DropDown()
self.semtler.secim=None
illerIlcelerAcilirKutu().run()
|
ve kv
dosyası:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <illerIlcelerAcilirKutu>:
BoxLayout:
Button:
id: iller
text: "İl Seçiniz"
on_release: app.iller.open(self)
size_hint_y: None
height: 30
Button:
id: ilceler
text: "İlçe Seçiniz"
on_release: app.ilceler.open(self)
size_hint_y: None
height: 30
Button:
id: semtler
text: "Semt Seçiniz"
on_release: app.semtler.open(self)
size_hint_y: None
height: 30
|
Not
Derleme yaparken (Paket Derleme kısmında anlatılan) buildozer in paketin içine sqlite modülünü koyması için requirements
seçeneğinin şu şekilde yapılandırılması gerekir:
requirements = kivy,sqlite3
Bunun dışında iller.db
dosyasının içerilebilmesi için source.include_exts
seçeneğinin sonuna db eklenemsi gerekir
(tüm db uzantılı dosyalar içerilecektir):
source.include_exts = py,png,jpg,kv,atlas,db
9.3. Eylem Çubuğu¶
Mobil programlamada eylem çubuğu (ActionBar), masaüstü programlardaki menu çubuğu ve/veya araç çubuğuna benzetelebilir (yapısal olarak farklı olasa da). Eylem çubuğunu genellikle pencerinin üstüne koyarız ve programın yapılandırılması veya diğer eylemler için birkaç düğme ve/veya düğme grubu koyarız. Böylelikle programın kullanılmasını sağlarız.
Bir eylem çubğu en azından bir eylem görünümü (ActionView) ve bunun içinde
önceki eylem (ActionPrevious) nesnesinden oluşmalıdır. Diğer bir deyişle bir eylem çubuğu oluşturmak için en azından
ActionBar
, ActionView
ve ActionPrevious
nesnelerini kullanmamız gerekecek. Bu bilgilerden sonra hemen bir eylem çubuğu
oluşturan program yazalım (Liste 9.12)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.actionbar import ActionBar
from kivy.uix.actionbar import ActionView
from kivy.uix.actionbar import ActionButton
from kivy.uix.actionbar import ActionPrevious
class eylemCubugu(App):
def build(self):
eylemcubugu= ActionBar(pos_hint= {'top':1})
eylemgorunumu=ActionView()
eylemcubugu.add_widget(eylemgorunumu)
oncekieylem=ActionPrevious(title='Eylem Çubuğu')
eylemgorunumu.add_widget(oncekieylem)
duzen = BoxLayout(orientation='vertical')
duzen.add_widget(eylemcubugu)
self.etiket=Label(text="Ana Alan")
duzen.add_widget(self.etiket)
return duzen
eylemCubugu().run()
|
Burada gördüğünüz gibi öncelikle bir eylem çubuğu (ActionBar
) oluşturuyoruz. Bu çubuğun, bulunduğu alandaki konumunu üst taraf
yapmak için pos_hint parametresine {'top':1}
değerini veriyoruz. Önceki eylem (ActionPrevious
) nesnesinin title
parametresine genellikle programın adını veriririz (unutmayın mobil cihazlarda pencere başlığı olmayacaktır). Yatay yerleşimli
kutu pencere düzeninin ilk elemanı eylem çubuğu, ikinci elemanı ise bir etiket oldu. İkinci eleman yerine genellikle bir başka pencere
düzeni koymayı yeğleriz ki, programımızın ana penceresinde görünmesini istediğimiz nesneleri yerleştirelim. Programımızı çalıştırdığımızda
elde edeceğimiz pencere Şekil 9.10 ‘da verilmiştir.
Burada başlığın solunda bir simgenin olduğunu görüyorsunuz. Eğer önceki eylem (ActionPrevious
) nesnesinin app_icon
parametresine bir simge vermez isek ön tanımlı olarak kivy simgesi görünür. Bu simgenin nereden geldiğini daha donra açıklayacağız.
İsterseniz şimdi bu simgeyi değiştirelim. Bunun için main.py
programını kaydettiğiniz yere şu resmi indirin:
document-edit.png: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/6/document-edit.png
Daha sonra 21. satırdaki ActionPrevious
nesnesinin aşağıdaki gibi app_icon
parametresine document-edit.png
verin:
oncekieylem=ActionPrevious(title='Eylem Çubuğu', app_icon='document-edit.png')
9.3.1. atlas¶
Yukarıdaki değişikliği yapıp programı çalıştırırsanız soldaki simgenin değiştiğini göreceksiniz. Peki bu ön tanımlı simge nereden geliyor? Cevabını vereyim atlas
dan.
Gerçekten atlas nesnesinden geliyor. atlas
nesnesi programda kullanacağınız simgeleri bir tek resim haline getirip, bunu
indeksleyip daha sonra bu simgeleri kullanmamızı sağlar. Elbetteki atlas
nesnesinin ön tanımlı bir simge seti var. Kivy’nin
kurulu olduğu dizindeki data/images
klasöründe (benim Linux’umda tam patika /usr/lib/python2.7/dist-packages/kivy/data/images
,
Windows’umda tam patika C:\Python27\Lib\site-packages\kivy\data\images
) bulunan defaulttheme-0.png
dosyası kullanılan
ön tanımlı simgelerin birleşmesinden oluşan dosya, bu dosyanın ineksi ise defaulttheme.atlas
dosyasında bulunmaktadır.
defaulttheme.atlas
dosyası bildiğimiz Python sözlüğüdür. Bu sözlüğün içinde çeşitli simge gruplarından oluşan
indeksler bulunur. Örneğin defaulttheme-0.png
anahtarının değeri yine bir szölük ve burada simgelerin konumları bulunmaktadır.
Bir örnek verecek olursak audio-volume-medium
simgesi [52, 171, 48, 48]
konumunda bulunmaktadır. Bir programı
apk paketi haline getirdiğinizde atlas’ın ön tanımlı dosyasıda içerisinde bulunduğundan buradaki simgeleri istediğiniz yerde
kullanabilirsiniz. Şimdi document-edit.png
yerine atlas’ın içerisinde bulunan close
simgesini koyalım, bunun için
Liste 9.12 deki 21. satırı aşağıdaki gibi değiştirin:
oncekieylem=ActionPrevious(title='Eylem Çubuğu', app_icon='atlas://data/images/defaulttheme/close')
atlas’ın simgelerinin kullanımını şöyle açıklayabiliriz: /atlas/icin/patika/atlas_dosyasi/simge_adi Burada şuna dikkat etmelisiniz
simge_adi
sadece ismi içerir, dosya uzantısını içermez. Bu ön tanımlı simgeleri programınıza herhangi bir ekleme yapmadan
kullanabilir, öylece paketleyebilirsiniz.
Şimdi kendi atlas’ımızı oluşturalım. Bunun için şu dosyayı indirin ve programınızın (mevcut main.py
dosyasının olduğu) patikaya açın:
simgeler.png: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/6/simgeler.zip
Şimdi komut satırından (cmd
) aşağıdaki komutu çalıştırın [1] [2] :
$ python -m kivy.atlas atlasim 256x256 simgeler/*.png
Bu komut bulunduğunuz dizinde iki adet dosya oluşturacaktır. Birincisi atlas indeksi (atlasim.atlas
), diğeri simgelerin birleştirildiği
256x256 boyutlarında bir PNG dosyası olan atlas resimidir (atlasim-0.png
). Çalıştırdığımız komutun ilk kısmını zaten biliyorsunuz, atlas’ın
nasıl çalıştığını anlatalım:
- İlk aldığı argüman isimdir. Burada
atlasim
ismini verdik. Daha sonra kullanırken bu ismi yazacağız. - İkinci argüman oluşturulacak atlas resminin boyutlarıdır. Eğer resimleriniz büyükse daha büyük bir boyut kullanabilirsiniz. enxboy şeklinde yazılır
- Üçüncü argüman ise atlas’a eklenecek simgelerdir. Burada bulunduğumuz dizindeki
simgeler
dizini altındaki tüm PNG dosyalarını eklemesini istedik.
Şimdi oluşturduğumuz atlası Liste 9.12 deki 21. satırı aşağıdaki gibi değiştirerek kullanalım:
oncekieylem=ActionPrevious(title='Eylem Çubuğu', app_icon='atlas://atlasim/document-edit')
Unutmayın atlasim-0.png
ve atlasim.atlas
doslayalrı Liste 9.12 programının kayıtlı olduğu dizinde olmalıdır.
Burada document-edit.png
simgesini kullanmak için sadece dosya adını (document-edit
) yazdığımızıda aklınızdan çıkarmayın.
Oluşturduğumuz atlasim.atlas
ve atlasim-0.png
dosyalarını şuradan indirebilirsiniz:
atlasim.atlas: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/6/atlasim.atlas
atlasim-0.png: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/6/atlasim-0.png
Not
Derleme yaparken (Paket Derleme kısmında anlatılan), buildozer.spec
dosyasında herhangi bir değişiklik yapmanıza gerek yok.
Çünkü, buildozer.spec
dosyasındaki source.include_exts
yapılandırma seçeneği ön tanımlı olarak atlas
ve png
doslayarını
paketin içerisine koyacak şekilde yapılandırılmıştır.
9.3.2. Eylemler¶
Aslında eylem öncesine, tıklandığında eylemlerin gerçekleşeceği birtakım nesnelerin yerleştirilmesi gerekiyor. Bunu bildiğimiz düğmeler (Button) kullanarak yapabilirsiniz. Ancak bunun yerine eylem düğmesi (ActionButton) kullanmanızı tavsiye ederim. Eylem düğmeleri, normal bir düğme gibi çalışır. Buna ilaveten bir de simge yerleştirebilirsiniz. Masaüstü programların araç çubuğunda bulunan düğmelere benzetebilirsiniz. Şimdi bunlardan birini ekleyelim. Bunun için yapmanız gereken Liste 9.12 de 23. satırdan sonra şunları yazmak:
ac_eylem_dugmesi=ActionButton(text="Aç", icon='atlas://atlasim/document-open')
eylemgorunumu.add_widget(ac_eylem_dugmesi)
Bu iki satırı eklediğinizde, eylem çubuğunun sağında üzerinde simgesinin bulunduğu bir düğme eklenecektir. Elbetteki düğmemizin şimdilik bir görevi yok.
Programımızı kv
dili ile yazıp rahata erelim. Python programı şu şekilde olmalıdır (Liste 9.13):
1 2 3 4 5 6 7 8 9 10 | # -*- coding: utf-8 -*-
from kivy.app import App
class eylemCubugu(App):
def build(self):
pass
eylemCubugu().run()
|
ve kv
dosyası (Liste 9.14):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <eylemCubugu>:
BoxLayout:
orientation: 'vertical'
pos_hint: {'top':1}
ActionBar:
ActionView:
ActionPrevious:
title: 'Metin Düzenleyici'
with_previous: False
app_icon: 'atlas://atlasim/document-edit'
ActionButton:
text: 'Aç'
id: ac_eylem_dugmesi
icon: 'atlas://atlasim/document-open'
ActionButton:
text: 'Kaydet'
id: kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save'
ActionButton:
text: 'Farklı Kaydet'
id: farklı_kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save-as'
ActionButton:
text: 'Yeni'
id: yeni_eylem_dugmesi
icon: 'atlas://atlasim/document-new'
ActionButton:
text: 'Çık'
id: yeni_eylem_dugmesi
icon: 'atlas://atlasim/application-exit'
Label:
text: 'Ana Alan'
|
Hiçbir iş yapmayan programımızı çalıştırdığımızda Şekil 9.11 daki görüntüyü elde edeceğiz.
Sanırım neden bu düğmeleri seçtiğimizi anladınız. Aslında eylem düğmelerini, normal düğmeleri (Button) kullandığınız yerlerin çoğunda
kullanma şansınız var. Burada eylem öncesi nesnesinin (ActionPrevious) simgesinin solunda “<” var idi. Bunu kaldırmak için bu nesnenin
with_previous özelliğine False
değerini verdik.
9.3.3. Metin Düzenleyici Yeniden¶
Şimdi hazırladığımız eylem çubuğu ile Metin Düzenleyici bölümünde hazırladığımız metin düzenleyiciyi
birleştirelim. Öncelikle kv
dosyasını düzenleyelim. Bunun için metinduzenleyici.kv
dosyasındaki <metinDuzenleyici>:
formunda (dosyanın başındaki form) aşağıdaki satırları silin:
BoxLayout:
size_hint_y: 10
Button:
text: "Aç"
on_press: app.dosyaAcIsleviDialog()
Button:
text: "Kaydet"
on_press: app.dosyaKaydetIslevi()
Button:
text: "Farklı Kaydet"
on_press: app.farkliKaydetDialog()
Button:
text: "Yeni"
on_press: app.yeniDosyaAcIslevi()
Button:
id: cik_dugmesi
size_hint_x: .15
background_color: (0, 1, 0, 1)
on_press: app.cik()
ve bunların yerine TextInput:
satırından önce (exit_on_escape: False
dan sonra) aşağıdakileri yazın:
ActionBar:
pos_hint: {'top':1}
ActionView:
ActionPrevious:
title: 'Metin Düzenleyici'
id: eylem_oncesi
with_previous: False
app_icon: 'atlas://atlasim/document-edit'
ActionButton:
text: 'Aç'
id: ac_eylem_dugmesi
icon: 'atlas://atlasim/document-open'
on_press: app.dosyaAcIsleviDialog()
ActionButton:
text: 'Kaydet'
id: kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save'
on_press: app.dosyaKaydetIslevi()
ActionButton:
text: 'Farklı Kaydet'
id: farklı_kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save-as'
on_press: app.farkliKaydetDialog()
ActionButton:
text: 'Yeni'
id: yeni_eylem_dugmesi
icon: 'atlas://atlasim/document-new'
on_press: app.yeniDosyaAcIslevi()
ActionButton:
text: 'Çık'
id: cik_dugmesi
icon: 'atlas://atlasim/application-exit'
on_press: app.cik()
ve son olarak main.py
dosyasında çıkış düğmesinin rengini değiştirdiğimiz yerlerde, renk değişikliği yerine simge değişikliği
yapabilmek için şu satırların yerine:
self.root.ids.cik_dugmesi.background_color = [0, 1, 0, 1]
şu satırı yazın (toplam 4 satır değişikliğ):
self.root.ids.cik_dugmesi.icon='atlas://atlasim/application-exit'
şu satır yerine:
self.root.ids.cik_dugmesi.background_color = [1, 0, 0, 1]
şu satırı yazın (1 satır değişikliği):
self.root.ids.cik_dugmesi.icon='atlas://atlasim/dialog-cancel'
yapacağınız değişikliklerin hepsi bu kadar. Elbetteki atlasim.atlas
ve atlasim-0.png
dosyalarını metin düzenleyici programımızın
olduğu dizine kopyalamayı unutmayacağız. Şimdi metin düzenleyici programımız daha çekici oldu. Programımızı çalıştırdığımızda
Şekil 9.12 daki görüntüyü elde edeceğiz.
Programın son halini şuradan alabilirsiniz:
atlasim.atlas: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/8/atlasim.atlas
atlasim-0.png: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/8/atlasim-0.png
metinduzenleyici.kv: https://raw.githubusercontent.com/mbaser/kivy-tr/master/docs/programlar/listeEylem/programlar/8/metinduzenleyici.kv
Soru: Şekil 9.12 deki ekran görüntüsününde eylem öncesi nesnesinin başlığı, açılan (ya da kaydedilen)
dosyanın dosya adını göstermektedir. Bunu siz de yapabilirmisiniz? İpucu: self.root.ids.eylem_oncesi.title
metnini dosya adı yapmalısınız.
Metin düzenleyicinin son halini derledim ve denemeniz için aşağıdaki köprüye koydum:
Programın Android öykünücüdeki çalışan hali ise şöyle:
9.3.4. Eylem Düğmelerini Ayırma ve Gruplama¶
Eylem düğmeleri arasına ayıraç koyarak, tıpkı masaüstü programlarda bulunan araç çubuklarında olduğu gibi, birbirinden ayırabilirsiniz.
Ayıraç koymak istediğiniz düğmelerin arasına (kv
dosyasında) ActionSeparator nesnesi koymalısınız. Örneğin “Kaydet” ve “Farklı Kaydet”
arasına ayıraç koymak için metinduzenleyici.kv
dosyasının ActionBar
kısımını aşağıdaki gibi düzenleyebilirsiniz:
ActionBar:
pos_hint: {'top':1}
ActionView:
ActionPrevious:
title: 'Metin Düzenleyici'
id: eylem_oncesi
with_previous: False
app_icon: 'atlas://atlasim/document-edit'
ActionButton:
text: 'Aç'
id: ac_eylem_dugmesi
icon: 'atlas://atlasim/document-open'
on_press: app.dosyaAcIsleviDialog()
ActionSeparator:
ActionButton:
text: 'Kaydet'
id: kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save'
on_press: app.dosyaKaydetIslevi()
ActionButton:
text: 'Farklı Kaydet'
id: farklı_kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save-as'
on_press: app.farkliKaydetDialog()
ActionSeparator:
ActionButton:
text: 'Yeni'
id: yeni_eylem_dugmesi
icon: 'atlas://atlasim/document-new'
on_press: app.yeniDosyaAcIslevi()
ActionButton:
text: 'Çık'
id: cik_dugmesi
icon: 'atlas://atlasim/application-exit'
on_press: app.cik()
Eylem düğmelerini gruplamak için ActionGroup nesnesini kullanabilirsiniz. Bu nesnenin mode parametresi
gruplamanın nasıl yapılacağını belirtir. Ön tanımlı değeri normal
dir ve ekranda yer olmadığı zaman gruplama yapar. Yeteri kadar
yer var ise gruplamadan gösterir. Değerini spinner
yaparsanız her zaman gruplanır ve simgeler yerine metin gösterilir. Elebetteki
text
parametresine grup metnini belirtmeliyiz. Aşağıda metinduzenleyici.kv
dosyasının ActionBar
kısımında nasıl
kullanılacağı gösterilmiştir:
ActionBar:
pos_hint: {'top':1}
ActionView:
ActionPrevious:
title: 'Metin Düzenleyici'
id: eylem_oncesi
with_previous: False
app_icon: 'atlas://atlasim/document-edit'
ActionButton:
text: 'Aç'
id: ac_eylem_dugmesi
icon: 'atlas://atlasim/document-open'
on_press: app.dosyaAcIsleviDialog()
ActionGroup:
text: 'Kaydetme'
mode: 'normal'
ActionButton:
text: 'Kaydet'
id: kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save'
on_press: app.dosyaKaydetIslevi()
ActionButton:
text: 'Farklı Kaydet'
id: farklı_kaydet_eylem_dugmesi
icon: 'atlas://atlasim/document-save-as'
on_press: app.farkliKaydetDialog()
ActionButton:
text: 'Yeni'
id: yeni_eylem_dugmesi
icon: 'atlas://atlasim/document-new'
on_press: app.yeniDosyaAcIslevi()
ActionButton:
text: 'Çık'
id: cik_dugmesi
icon: 'atlas://atlasim/application-exit'
on_press: app.cik()
Şekil 9.14 de üsstteki resimde pencere yeteri kadar genişken eylem düğmelerinin gruplanmamış hali, alttaki resimde ise pencere daraltıldığında gruplanmış hali görünüyor. Gruplanmış eylem düğmeleri eylem çubuğunun en sağında yer alır.
Dipnotlar
[1] | Sismteminizde PIL kurulu değil ise komut satırından şu şekilde kurabilirsiniz: python -m pip install Pillow |
[2] | Windows kullanıcıları |