30 Nisan 2020 Perşembe

PySimpleGUI ile KOLAY PYTHON GUI


Merhaba , karantina günlerinde Rails için yazılar yazarken internette gözüme takılan bir şey çok ilgimi çekti. PySimpleGUI adında bir Python GUI kütüphanesi. Aynı Ruby'nin Shoes kütüphanesi gibi kolayca GUI uygulaması yapılabiliyor. Hatta bir konsol uygulamasında bile kullanıcıya soru sormak, dosya seçtirmek vs işler için kolayca kullanılabilir.

Hadi beraber kurcalayalım. İlk önce kurulum tabi ki.

    
    pip install pysimplegui
    veya
    pip3 install pysimplegui
    

Reçete-1A Tek Atımlık Pencere (en basit tasarım)





Bu pencereyi sadece bir işlem yapıp kapatacağınız zamanlar kullanacağınız en genel patern (Soru sormak, bilgilendirmek vs). Bu uygulamanın kodu şöyle:


import PySimpleGUI as sg

layout = [[sg.Text("Tek atımlık pencerem.")],
        [sg.InputText()],
        [sg.Submit(), sg.Cancel()]]

window = sg.Window("Pencere Etiketi", layout)

event, values = window.read()
window.close()

text_input = values[0]
sg.popup(text_input, " girdiniz")


Bir pencereyi okuduğumuzda (read metodu), bize bir tuple döner. İlk eleman olayı içerir, o yüzden event değişkenine aldık. İkinci elemansa kullanıcı tarafından giriş yapılabilen elemanların değerlerini içeren bir dictionary'dir, bunu da values değişkenine aldık.

event okuma işleminden çıkışa sebep olan olaydır. Bir butona basılması, listeden bir eleman seçilmesi vs. Ayrıca pencere "X" tıklanarak kapatılırsa event değeri None olur.

values tüm input stili elemanların değerlerini içeren bir dictionary'dir. Elemanların her birinin key özelliğine bağlanarak değerler kaydedilir. Eğer elemanda key özelliği tanımlanmamışsa sırayla 0'dan başlayarak otomatik key'ler sayılar şeklinde verilir. Yukarıda da layout içinde InputText elemanına bir key özelliği tanımlamadığımız için otomatik verilen sayı ile değerini alıyoruz. Yegane Input elemanı olan InputText elemanının değeri values[0] olarak kaydedilmiş olacaktır.

Otomatik üretilen key yerine kendimiz bir key tanımlamak ve kodu daha okunabilir yapmak istersek şöyle yazmalıyız.

import PySimpleGUI as sg

layout = [[sg.Text("Tek atımlık pencerem.")],
        [sg.InputText(key="-IN-")],
        [sg.Submit(), sg.Cancel()]]

window = sg.Window("Pencere Etiketi", layout)

event, values = window.read()
window.close()

text_input = values["-IN-"]
sg.popup(text_input, " girdiniz")




Reçete-1B Tek Atımlık Pencere (tek satırla)


Aşağıdaki gibi tek satırla da otomatik kapanan bir pencere tanımlayabiliriz. Özellikle başka bir uygulama içersinde kullanıcıdan veri almak ya da bildiri yapmak için kullanılabilir.

import PySimpleGUI as sg

event, values = sg.Window("Login Penceresi",
                [[sg.T("Login ID giriniz"), sg.In(key="-ID-")],
                [sg.B("Tamam"), sg.B("İptal")]]).read(close=True)

login_id = values["-ID-"]


Burada farklı olan şey close=True bildirimi. Bu sayede read metodu değer dönmeden hemen önce pencere kapatılıyor. Tabii nasıl tek satır derseniz, yerleşim açık okunabilsin diye tek satır kodu 3 satıra dağıttık.

Bir de Button yerine B , Text yerine T , InputText yerine In yazdık. Bunlar bu eleman isimlerinin kısaltmaları. Özellikle karmaşık yerleşimlerde tüm yerleşimi tek bir ekranda görebilmek için bu kısaltmalar çok faydalı olur. Zaten çok az kod yazarak GUI oluşturuyoruz, bir de bu kısaltmalar var. Demek tasarlayan kişi benim gibi tembel birisiymiş.



Reçete-2A Kalıcı Pencere (döngüyle defalarca okuma yapmak)



Daha gelişmiş bir tipik pencere de ekranı kapatmadan açık kalan ve verileri toplayıp işlemeye devam eden bir penceredir. Yani tipik bir uygulama penceresi. Aşağıdaki 10 satırlık kod ile çalışan ve kullanıcı ile iletişime girip işleyen bir program yazabiliyoruz.

Bu kod bir pencere açıyor ve kullanıcının girdiği değerleri etkileşim değerleri ile birlikte konsola yazıyor.

import PySimpleGUI as sg

sg.theme("DarkAmber")   # Kullanıcıların ilgisini taze tut

layout = [[sg.Text("Kalıcı Pencere")],
        [sg.Input(key="-IN-")],
        [sg.Button("Oku"), sg.Exit()]]

window = sg.Window("Açık kalan pencere", layout)

while True:     # Olaylar döngüsü
    event, values = window.read()
    print(event, values)
    if event in (None, "Exit"):
        break

window.close()


Bu uygulamadan çıkan bir örnek şöyle olabilir

PS E:\ujk_common\prg\python\PySimpleGui\blog_apps> python PersistentWindow.py
Oku {'-IN-': 'buraya değer girdim'}
Oku {'-IN-': 'daha da yazdım'}
Exit {'-IN-': 'Artık çıkıyorum Exit butona basarım'}
PS E:\ujk_common\prg\python\PySimpleGui\blog_apps>


Bu programda event tıklanan butonların isimleri oluyor. Arkasından values değişkeni basılıyor. Bu örnekte değeri olan tek eleman sg.Input elemanı. Görüldüğü gibi Input elemanını tanımlarken verdiğimiz key değeri sonuç dictionary'de karşımıza çıkıyor. Eğer pencereyi "X" tıklayarak kapatırsak şöyle bir çıktı alırız:

None {'-IN-': None}

event değişkenine gelen bu None değeri aslında pencerenin kullanıcı tarafından kapatıldığını anlamak için çok yararlıdır.


Reçete-2B Kalıcı Pencere (okunan değeri pencere üzerinde yazmak)




Bu daha gerçekçi bir uygulama , kullanıcının girdiği değer pencere içinde kullanılıyor. Biraz daha karışık bir program.

Programımızın kodu şöyle:

PersistentWindow2.py
import PySimpleGUI as sg

sg.theme("BluePurple")

layout = [[sg.Text("Girdiğiniz yazı burada görülecek : "), sg.Text(size=(20,1), key="-OUTPUT-")],
        [sg.Input(key="-IN-")],
        [sg.Button("Göster"), sg.Button("Çıkış")]]

window = sg.Window("Reçete 2B", layout)

while True:   # olaylar döngüsü
    event, values = window.read()
    print(event, values)
    if event in (None, "Çıkış"):
        break
    if event == "Göster":
        window["-OUTPUT-"].update(values["-IN-"])

window.close()

Pencerede bir elemanın içeriğini değiştirmek için onun update() metodunu çağırırız. Yukarıdaki kodda window["-OUTPUT-"] ile key değeri "-OUTPUT-" olan eleman seçiliyor. Sonra key değeri "-IN-" olan elemanın içeriği update() metodu ile "-OUTPUT-" elemanına yazılıyor.



Pencereden çıkış


Yukarıdaki programda 

    if event in (None, "Çıkış"):
        break

satırları ile kullanıcıya bir çıkış yolu sağlıyoruz. Bunu sağlamazsak kullanıcı "Görev Yöneticisine falan başvuracaktır. Her zaman penceremizden ve uygulamamızdan kontrolümüz altında bir çıkış sağlamalıyız.

window.close() metodu ile pencereleri işimiz bittiğinde mutlaka yok etmeliyiz. Bazen pencerenin ekrandan yok olması hafızadan da yok olması anlamına gelmez.


Kodlama Alışkanlıkları


Bazı kodlama düzenlerini alışkanlık haline getirmemiz iyi olacak. Yukarıdaki programları hiç bir şey değiştirmeden kopyalayıp yapıştırırsak, PySimpleGUI programlarını tanıyan biri programın ne yaptığını anlamakta zorluk çekmeyecektir. 

Genel olarak şu alışkanlıklar tavsiye edilir:
  • import PySimpleGUI as sg
  • Pencerenize window adını verin
  • Okuma işleminden dönen değerleri event ve values değişkenlerine koyun
  • Yerleşiminize layout adını verin
  • Elemanları seçerken window[key] yapısını kullanın
  • Key değerleri için "-KEY-" formatına sadık kalın
Tabi ki bunlar tavsiye, hiç birine uymak zorunda değilsiniz. Ancak bunlara uyarsanız başkaları daha kolay programınızı anlayacaktır. 


Kodlama İpuçları


Bu yazıları yazan PySimpleGUI uzmanı birkaç tavsiyede daha bulunmuş:
  • Her şart altında basit düşüncede kalmaya çalışın
  • Dökümanları okuyun ve araştırın (http://www.PySimpleGUI.org)
  • Yukarıda anlatılan alışkanlıkları uygulayın
  • Yerleşimleri kompakt yazın
  • Elemanlarda hep aynı kodları tekrarlıyorsanız "kullanıcı elemanları" tanımlayın (eleman geri dönen fonksiyonlar)
  • Diğer GUI kütüphanelerinin yapılarını taklit etmeyin, PySimpleGUI yapısını kullanın
  • Makul zaman aşımı değerleri kullanın (mümkün olduğunca sıfırdan büyük olmayan değerler ... iyi bir CPU dostu olun)
  • Bir thread'dan herhangi bir PySimpleGUI çağrısı yapmaya çalışmayın
  • Çıkmadan önce pencereyi kapatın
  • Doğrusal olay döngüleri yazın
  • Element.get terine values dictionary kullanın
  • Daha fazla ipuçaları ve teknikler için Demo Programları ziyaret edin
Bir iki tanesini açalım

Kompakt yerleşimler oluşturun


Yerleşim satırlarının (layout) tek ekranda tamamen görünmesi için çaba sarfedin. Her satıra bütün parametreleri yazmayın, tonlarca boşluk bırakmayın.

Çok fazla eleman varsa kısaltılmış isimleri kullanın (örn. sg.Button yerine sg.B yazarak 5 karakter kazanırsınız)

Burada amaç tüm yerleşimi yukarı-aşağı kaymadan görebilmek


PySimpleGUI yapılarını kullanın


PySimpleGUI programları diğer OOP Python GUI'ler ile benzer yapıda değildir. Onu OOP tasarıma zorlamak bir sürü self. kelimesinden başka birşey kazandırmayacaktır. Çok daha karmaşık olacak ve kafa karıştıracaktır.

Tabii ki geri kalan kısımı OOP yazabilirsiniz.

Şunu belirtelim ki "App" benzeri bir konsept , bitmeyen olay döngüleri ve caal-back fonksiyonları yoktur. PySimpleGUI diğer PyQT veta Tkinter'den farklıdır. Eğer başlangıç olarak Window adında bir alt sınıf yapmaya çalışıyorsanız bir şeyler  ters gidiyordur.




Temalar - Pencere güzelliği


Bir tek satır ile pencerelerin renklerini temalar yardımıyla değiştiririz. 

Tkinter için en çok söylenen şey şey çirkinliğidir. Bunun yerine PySimpleGUI temalarını kullanarak sorunu çözebiliriz.

sg.theme('Dark Green 5')


theme() metodu çağırılması ile tüm yazı rengi , arkaplan rengi , buton rengi , input rengi gibi 13 parametre birden değişir.

Default tema "DarkBlue3" tema'sıdır. Mevcut 140 tane tema var bunları şu resimde inceleyebilirsiniz.



Yukarıdaki görseli kendi kurulumunuzda görmek için

sg.theme_previewer()


satırı ile açılacak pencerede görebiliriz.




Reçete - Tema Gösterici



Listeden herhangi bir elemana tıklarsak o temaya ait bir diyalog penceresi örnek olarak açılacaktır.


ThemeBrowser.py
import PySimpleGUI as sg

sg.theme("DarkBrown")

layout = [[sg.Text("Tema Gösterici")],
        [sg.Text("Demo penceresini görmek için listeden birini seçin")],
        [sg.Listbox(values=sg.theme_list(), size=(20, 12), key="-LIST-", enable_events=True)],
        [sg.Button("Çıkış")]]

window = sg.Window("Tema Gösterici", layout)

while True:
    event, values = window.read()
    if event in (None, "Çıkış"):
        break
    sg.theme(values["-LIST-"][0])
    sg.popup_get_text("Bu tema {}".format(values["-LIST-"][0]))

window.close()




Reçete - Temayı modifiye etmek


Diyelim LightGreen3 temasını kullanacağız ama butonların yazılarının beyaz değil siyah olmasını istiyoruz. Bir tema ayarını görmek için kullanılan metod aynı zamanda o ayarı değiştirmek için de kullanılıt. Örneğin theme_background_color() metodu temanın arkaplan rengini bize geri dönerken , aynı metodu parametresine "blue" değeri vererek çağırmak - yani theme_background_color("blue") - arkaplan rengini mavi olarak değiştirecektir. 

Şöyle bir kod yazalım:

ModifyTheme.py
import PySimpleGUI as sg

sg.theme("LightGreen3")
sg.popup_no_wait("Bu standart yüklenmiş LightGreen3 tema", "Buton yazıları beyaz")

# Temayı modifiye et
sg.theme_button_color(("black", "#6d9f85"))

sg.popup("Bu modifiye edilmiş LightGreen3 tema", "Buton yazıları siyah")


Bu kod temayı değiştirmeden önce ve değiştirdikten sonra olmak üzere iki popup pencere açarak bize değişimi gösteriyor.






Reçete - Kendi renk temamızı eklemek


Temayı değiştirdikten sonraki tüm pencerelerde değişikliklerimiz geçerli olacaktır. Ama her seferinde mevcut bir temayı tekrar tekrar modifiye etmek yerine kendi temamızı da tanımlayabiliriz. 

Aşağıdaki kodu yazalım:

NewTheme.py
import PySimpleGUI as sg

# yeni tema renkleri ve ayarlarını ekleyelim
sg.LOOK_AND_FEEL_TABLE["YeniTema"] = {"BACKGROUND": "#709053",
                                    "TEXT": "#fff4c9",
                                    "INPUT": "#c7e78b",
                                    "TEXT_INPUT": "#000000",
                                    "SCROLL": "#c7e78b",
                                    "BUTTON": ("white", "#709053"),
                                    "PROGRESS": ("#01826b", "d0d0d0"),
                                    "BORDER": 1, "SLIDER_DEPTH": 0,
                                    "PROGRESS_DEPTH": 0
                                    }
                                
# yeni temaya geçelim
sg.theme("YeniTema")
# bir deneme ile görelim
sg.popup_get_text("Yeni temanın görünüşü böyle")




Pencereleri giydirmenin başka yolları


Renklere ilave olarak pencerelerimizi daha yakışıklı yapmak için başka yollar da var. Bunlardan bazıları:
  • Title Bar'ı yok etmek
  • Pencereyi yarı transparan yapmak (opacity)
  • Normal butonları grafiklerle değiştirmek
Bu 3 seçeneğin kombinasyonlarını kullanarak Rainmeter stili pencereler elde edebiliriz. 

Aşağıdaki pencere bir örnek gösteriyor. Gördüğünüz gibi pencere arkasından bir yazı silüeti görünüyor, çünkü "alpha" kanalı ayarlanarak pencerenin yarı şeffaf olması sağlanmış. Pencerede Title Bar yok ve kapatmak için kırmızı bir grafik "X" butonuna sahip.




Title Bar'ı yok etmek ve yarı transparan yapmak


Bu iki özellik de pencereyi oluşturan kod içinde ayarlanabilir. no_titlebar ve alpha_channel parametreleri bu işe yarar.

Fakat daha önce birşeyi düşünmeliyiz. Title Bar'ı yok edersek pencere nasıl taşınacak ve nasıl kapatılacak? Taşıma işlemini gerçekleştirmek için grab_anywhere parametresini kullanabiliriz. Bu parametre True yapıldığında pencerede herhangi bir yerden tutarak aynı Title Bar'dan tutmuş gibi ekranda taşıyabiliriz. Bu parametreyi kullanmak içim Title Bar'ı yok etmemiz gerekmez o varken de kullanabiliriz.

Pencereyi yarı transparan yapmak için alpha_channel parametresine değer girilir. Değer 0 ila 1 arasında bir noktalı sayı olacaktır. Yukarıdakine benzer bir pencere için pencerenin kodu şöyle olacaktır.

window = sg.Window("PSG System Dashboard", layout, 
          no_titlebar=True, alpha_channel=.5, grab_anywhere=True)




Butonu grafikle değiştirmek


PySimpleGUI'de PNG ve GIR resimleri buton olarak kullanabiliriz. Ayrıca bu dosyaları Base64 formata çevirip direk kodumuzun içine de koyabiliriz.

Bir butonu grafik yapmak için 4 adımlık bir yapılacak listemiz var
  1. PNG ya da GIF grafik bulunur (veya kendimiz yaparız)
  2. Grafik Base64 string formata çevrilir
  3. Base64 string programımıza değişken olarak eklenir
  4. Butonu tanımlarken bu değişken butonun grafiği olarak belirtilir.

Adım-1 Grafiği bulmak

Tabii bir sürü yol var. Burada bir listede çok yararlı siteler bulabilirsiniz. Google'da "red X icon" arayıp görsel seçeneklerinde "Transparent Background" seçerek de bir çok ikon görseline ulaşabiliriz. (Yaşasın internet!). 

Örnek olarak şu ikonu alalım. 

Adım-2 Base64 string formata çevirmek

PySimpleGUI Github reposundaki demo programlardan birisi de "Demo_Base64_Image_Encoder.py" adındaki program. Bu program klasördeki tüm resim dosyalarını dönüştürüp sonuçları output.py isimli dosyaya kaydediyor. 

Başka bir demo programı olan "Demo_Base64_Single_Image_Encoder.py" ise verilen resim dosyasını Base64 format veriye dönüştürüyor. Oradan kopyalayıp programınıza yapıştırabilirsiniz.

Bundan başka https://base64.guru/converter/encode/image gibi online sitelerden de yararlanabiliriz. 

Bu sitede dönüştürelim Önce datatype için Remote url seçiyoruz sonra da resim url adresini kopyalayıp yapıştırıyoruz. Sonra da "Encode image to Base64" düğmesine tıklıyoruz. 


Size tavsiyem ikon dosyasını indirin ve boyutunu 50x50 piksele küçültün ya da aşağıdaki resmi indirin. Yoksa çok kocaman bir butonunuz olacak.


Adım-3 Base64 string değişkeni eklemek

Size tavsiyem ikon dosyasını indirin ve boyutunu 50x50 piksele küçültün ya da resmi indirin. Yoksa çok kocaman bir butonunuz olacak. Base64 stringe dönüşmüş verimizi aşağıdaki değişkene yapıştırıyoruz.

red_x_base64 = b'iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAjiKvP/9RDMrnS ...'



Adım-4 Butonumuzda Base64 stringi kullanmak


Buton arkaplan renginin görünmesini istemediğimiz için tema arkaplan rengi ile aynı yapıyoruz. 

import PySimpleGUI as sg

red_x_base64 = b'iVBORw0KGgoAAAA ... '

layout = [[sg.Text("sınırları olmayan ve bir grafik butonu olan pencere")],
        [sg.Button("", image_data=red_x_base64,
        button_color=(sg.theme_background_color(), sg.theme_background_color()),
        border_width=0, key="Çıkış")]]
    
window = sg.Window("Title", layout, no_titlebar=True, 
    alpha_channel=.8, grab_anywhere=True)
    
while True:
    event, values = window.read()
    print(event, values)
    if event in (None, "Çıkış"):
        break

window.close()


Ben bu kod içine resim gömme işini hiç sevmiyorum. Dosyadan resim kullanmayı tercih ederim. Diyorsanız image_data özelliği yerine image_filename özelliği kullanabilirsiniz.

import PySimpleGUI as sg

layout = [[sg.Text("sınırları olmayan ve bir grafik butonu olan pencere")],
        [sg.Button("", image_filename="images/Red-X-icon-50x50.png",
        button_color=(sg.theme_background_color(), sg.theme_background_color()),
        border_width=0, key="Çıkış")]]

window = sg.Window("Title", layout, no_titlebar=True, 
    alpha_channel=.8, grab_anywhere=True)

while True:
    event, values = window.read()
    print(event, values)
    if event in (None, "Çıkış"):
        break

window.close()




Reçete-1 Tek atım pencere - Basit veri girişi - Otomatik numaralanmış


Eğer forma key değeri girilmemiş bir giriş elemanı eklersek key değeri otomatik olarak 0'dan başlayan numaralar şeklinde verilecektir. 

Bu örnekte 3 tane giriş elemanı ekleyeceğiz ve numaralar 0,1,2 olacaktır. İlk eklenen eleman values[0] olarak değeri okunabilir ve diğerleri de sırasıyla okunabilir. Şöyle bir pencere tasarlayalım.



OneShot3.py
import PySimpleGUI as sg

sg.theme("Topanga")

layout = [
    [sg.Text("Lütfen adınızı, adresinizi ve telefonunuzu girin")],
    [sg.Text("İsim", size=(15, 1)), sg.InputText()],
    [sg.Text("Adres", size=(15, 1)), sg.InputText()],
    [sg.Text("Telefon", size=(15, 1)), sg.InputText()],
    [sg.Submit(), sg.Cancel()]
]

window = sg.Window("Basit veri giriş ekranı", layout)

event, values = window.read()

window.close()

print(event, values[0], values[1], values[2])




Reçete - Scriptimize GUI eklemek


Mesela scriptimize komut satırında argüman olarak bir dosya adı girilmesi gerekiyor ancak kullanıcı argümanı girmeden scripti çalıştırdı. Hemen bir popup GUI açarak kullanıcıdan dosya seçmesini isteyebiliriz. 


FileOpen.py
import PySimpleGUI as sg
import sys

if len(sys.argv) == 1:
    event, values = sg.Window("Scripten istek",
                    [[sg.Text("Açılacak dosyayı seçiniz")],
                    [sg.In(), sg.FileBrowse()],
                    [sg.Open(), sg.Cancel()]]).read(close=True)

    fname = values[0]
else:
    fname = sys.argv[1]

if not fname:
    sg.popup("İptal", "Dosya ismi girilmedi")
    raise SystemExit("İptal ediliyor: dosya ismi verilmedi")
else:
    sg.popup("Seçtiğiniz dosya adı ", fname)




Reçete - Yukarıdakinin popup_get_file versiyonu


Tekerleği yeniden icat etmeye gerek yok, direk dosya ismi talep eden bir popup komutu var. Şöyle tek satır kodla bunu yapabiliriz:

fname = sg.popup_get_file("Açılacak dosyayı seçiniz")

Şu pencere açılır ve kullanıcının cevabını geri döner:


FileOpen2.py
import PySimpleGUI as sg
import sys

if len(sys.argv) == 1:
    fname = sg.popup_get_file("Açılacak dosyayı seçiniz")
else:
    fname = sys.argv[1]

if not fname:
    sg.popup("İptal", "Dosya ismi girilmedi")
    raise SystemExit("İptal ediliyor: dosya ismi verilmedi")
else:
    sg.popup("Seçtiğiniz dosya adı ", fname)




Reçete - Çok duyarlı girişler


Bazen kullanıcının birşeyleri seçtiğinde OK tuşuna basmadan seçimine göre birşeyler yapmak isteriz. Diyelim bir tane listbox var ve kullanıcı listeden bir elemanı seçecek. 

Responsive.py
import PySimpleGUI as sg

choices = ("Kırmızı", "Yeşil", "Mavi", "Sarı", "Turuncu", "Mor", "Kahverengi")

layout = [[sg.Text("Favori renginiz hangisi?")],
        [sg.Listbox(choices, size=(15, len(choices)), key="-COLOR-")],
        [sg.Button("OK")]]

window = sg.Window("Renk seçimi", layout)

while True:
    event, values = window.read()
    if event is None:
        break
    if event == "OK":
        if values["-COLOR-"]:
            sg.popup("Favori renginiz {}".format(values['-COLOR-'][0]))

window.close()


Bir rengi seçip OK tıkladığımızda şöyle bir cevap verecek.





enable_events kullanarak olay anını yakalamak


Bu OK tuşunu tıklamadan yapılan seçimi algılamamız gerekebilir. Mesela bazı web sitelerinde adres seçimi yaparken vilayet adını seçtikten sonra OK falan bir tuş tıklamadan önümüze o vilayetin ilçelerinin isimleri gelir. Burada seçimin yapıldığı an bir kod çalışmaya başlar. Biz de yukarıdaki programda OK tuşlanmadan seçim yapıldığı anda işlem yapabilmek için enable_events kullanarak listbox seçim anını yakalayabiliriz.

Şimdi Yukarıdaki programdan OK tuşunu kaldıralım ve listbox enable_events parametresini True yaparak seçim anını algılayalım:

Responsive.py
import PySimpleGUI as sg

choices = ("Kırmızı", "Yeşil", "Mavi", "Sarı", "Turuncu", "Mor", "Kahverengi")

layout = [
        [sg.Text("Favori renginiz hangisi?")],
        [sg.Listbox(choices, size=(15, len(choices)), key="-COLOR-", enable_events=True)]
        ]

window = sg.Window("Renk seçimi", layout)

while True:
    event, values = window.read()
    if event is None:
        break
    if values["-COLOR-"]:
        sg.popup("Favori renginiz {}".format(values['-COLOR-'][0]))

window.close()





Reçete - Giriş doğrulaması


Bazen kullanıcının yaptığı girişi belli özelliklere karşı test etmek gerekir. Mesela bir dosyaya verilecek şifrenin sadece rakamlardan oluşması ve 5 karakterden fazla olmamasını isteyebiliriz. 

Belki de sadece 0-9 arası sayılar , nokta ve eksi işaretlerinden oluşan bir noktalı sayı girilmesini isteyebiliriz. Kullanıcı her tuşa bastığında bu karakterlerden birine basmadıysa hemen silmek isteyebiliriz. 

Yukarıda olay anını yakalamayı öğrendik.Şimdi şu programa bakalım:

Restrict.py
import PySimpleGUI as sg

"""
    Bir input elemanına girilen karakterleri sınırlamak ve
    eğer girilen karakter yanlışsa son karakteri çıkartmak
"""

layout = [  [sg.Text("Sadece noktalı sayılar giriniz")],
            [sg.Input(key="-IN-", enable_events=True)],
            [sg.Exit()]  ]

window = sg.Window("Noktalı sayı giriş doğrulaması", layout)

while True:
    event, values = window.read()
    if event in (None, "Exit"):
        break
    if event == "-IN-" and values["-IN-"] and values["-IN-"][-1] not in ("0123456789.-"):
        window["-IN-"].update(values["-IN-"][:-1])

window.close()

Bu kod sadece doğru karakterlerin girilmesini kontrol ediyor. Ama tam olarak noktalı sayı girilmesini sağlayamıyor. Biraz daha geliştirelim. If kısmıyla biraz oynamamız gerekecek:

Restrict2.py
import PySimpleGUI as sg

"""
    Bir input elemanına girilen karakterleri sınırlamak ve
    eğer girilen karakter yanlışsa son karakteri çıkartmak
"""

layout = [  [sg.Text("Sadece noktalı sayılar giriniz")],
            [sg.Input(key="-IN-", enable_events=True)],
            [sg.Exit()]  ]

window = sg.Window("Noktalı sayı giriş doğrulaması", layout)

while True:
    event, values = window.read()
    if event in (None, "Exit"):
        break
    if event == "-IN-" and values["-IN-"]:
        try:
            in_as_float = float(values["-IN-"])
        except:
            if len(values["-IN-"]) == 1 and values["-IN-"][0] == "-":
                continue
            window["-IN-"].update(values["-IN-"][:-1])

window.close()


İşin çoğunu Python yaptı. girişteki değeri noktalı sayıya çevirmeyi deneyip eğer hata olursa son karakteri attık. Tek istisna var eğer ilk karakter olarak "-" girilmişse sadece "-" gören python yine hata verecektir. Bu durumda silmeden devam ediyoruz.





Reçete - Yazdırmak


Bir şeyleri ekrana yazdırmak programlarda çok kullanılan bir işlem. Birçoğumuzun ilk Python programı şöyle olmuştur:

print("Merhaba Dünya!")

Fakat bu konsolda oluyordu, GUI dünyasında nereye yazdıracağız? Pekala, bir çok yazdırabileceğimiz yer mevcut. Tabii ki önceki örneklerde gördüğümüz gibi, programımızı çağırdığımız konsolda da yazdırma imkanımız hala var.

Konsola yazdırmak uygulamayı direk GUI çalıştıran Pythonw komutuyla çalıştırdığımızda geçersiz olacaktır. Genelde de geliştirirken hariç , bitmiş programlarımızın yanında bir de konsol penceresi açık olsun istemeyiz. Endişeye gerek yok PySimpleGUI'de bir çok yöntem var.

Aşağıdaki reçeteler programımızda bulunan yazdırma işlemleri için değişik yöntemler sunuyor. Konsol için yazılmış bir programdaki print() komutlarımıza alternetif 3 yöntem şöyle:

  1. Debug penceresine yazdırmak
  2. Bir elemana yazdırmak
  3. Çok satırlı elemanlar


Reçete-1/3 Debug penceresine yazmak


Debug penceresi bir sanal konsol gibi çalışır. 2 çalışma modu vardır. Birinde normal çıktıyı da debug penceresine yönlendirir, diğerinde yapmaz. 

Print , eprint , EasyPrint komutları üçü de aynı işi yapar. Aşağıda Print ile bir örnek kod var. 

Yazdırma komutlarımızı debug penceresine yönlendirmenin bir yolu basit bir eşitlik ile standart print metodunun üzerine yazmak.

print = sg.Print

Başka bir yöntem de Print komutunu do_not_reroute_stdout = False parametre değeri ile çağırmak. Böyle yaptığımız satırdan sonraki standart print() metodları da debug penceresine yazacaktır.

DebugPrint.py
import PySimpleGUI as sg

sg.Print("stdout yönlendirme", do_not_reroute_stdout=False)
print("Bu normal print ama debug penceresine yönlendirilmiş")

sg.popup("Test")


Hem print hem de sg.Print debug penceresine yazıyorlar.

Renkli yazmak sadece standart çıkışı yönlendirmeyince geçerli olabilir.

Debug penceresine renkli satırlar yazmak istiyorsak yönlendirme yapmamalıyız.

DebugPrint2.py
import PySimpleGUI as sg

sg.Print("Bu yazı yeşil üzerine beyaz", text_color="white", background_color="green", font="Courier 10")
sg.Print("İlk satır değiştirilemeyen font özelliğini değiştirmeye çalışıyor")
sg.Print("Bu yazı print komutundaki gibi düz bir yazı")
sg.Print("Kırmızı üzerine beyaz", text_color="white", background_color="red")
sg.Print("Başka bir yazı", "sep gibi", "parametreler çalışır", sep=",")
sg.Print("Rengin sona kadar gitmesini istemiyorsak end parametresi", text_color="white", background_color="blue", end="")
sg.Print("\nBu yazının da rengi yok")

sg.popup("Test")


Son satırda "\n" koymasak önceki satır "end" parametresi ile bitirildiği için onun yanına yazılacaktı.



Reçete-2/3 Output elemanına yazmak


Eğer standart öıkışı penceremize yönlendirmek istiyorsak yerleşime bir tane Output elemanı eklemek yeterli olacaktır. Sonra kullanılan tüm print() metodları bu output elemanına yazacaktır. 

Aşağıdaki kod Go butonu tıklandıkça değerleri Output elemanına yazacaktır.

OutputPrint.py
import PySimpleGUI as sg

layout = [   [sg.Text("Tüm girdikleriniz aşağıda yazılacak:")],
            [sg.Output(size=(50, 10), key="-OUTPUT-")],
            [sg.In(key="-IN-")],
            [sg.Button("Go"), sg.Button("Clear"), sg.Button("Exit")]   ]

window = sg.Window("Pencere etiketi", layout)

while True:
    event, values = window.read()
    print(event, values)
    if event in (None, "Exit"):
        break
    if event == "Clear":
        window["-OUTPUT-"].update("")

window.close()




Reçete-3/3 Çok satırlı elemana yazdırmak


4.18.0 dan beri yerleşimdeki bir Multiline elemana yazdırılabiliyor. Multiline.print metodu Print metoduna benzer çalışıyor. Normal Print parametreleri sep ve end'e sahip. Atrıca renk opsiyonu da var. Güçlendirilmiş bir print fonksiyonu gibi. 

Bir Multiline elemanına yazdırma yönlendirmek için 2 yol var:
  1. Multiline.print metodunu kullanmak
  2. print metodunun üzerine yazmak
İlk seçenekle başlayalım. Standart print metodu başına bir Multiline eleman koyarak bunu yapıyoruz. Diyelim şöyle bir print komutumuz var

print("Test 1 2 3")

Eğer Multiline elemanımızın key değeri "-ML-" ise bunun seçimi de şöyle olacak

window("-ML-")

İkisini birleştirince Multiline print işlemini gerçekleştirmiş oluruz.

window("-ML-").print("Test 1 2 3")

Multiline elemanı sadece çıktı yazdırmak için kullanacağımızdan bunun window.read() metodu tarafından değişiminin algılanmasını istemeyiz. Bunu sağlamak için key değerine WRITE_ONLY_KEY sabitini eklemeliyiz.

window("-ML-"+sg.WRITE_ONLY_KEY).print("Test 1 2 3")

Aynı işlemi yerleşime Multiline elemanını eklerken de yapmamız gerekiyor.

Şimdi tüm bu anlatılanları bir programda toplayalım.

MultilinePrint.py
import PySimpleGUI as sg

layout = [   [sg.Text("Multiline elemanına yazdırma demosu")],
            [sg.MLine(size=(40, 8), key="-ML1-"+sg.WRITE_ONLY_KEY)],
            [sg.MLine(size=(40, 8), key="-ML2-"+sg.WRITE_ONLY_KEY)],
            [sg.Button("Go"), sg.Button("Exit")]   ]

window = sg.Window("Pencere etiketi", layout, finalize=True)

# olay döngüsüne girmeden bu satırlar işlensin diye finalize çağırdık
window["-ML1-"+sg.WRITE_ONLY_KEY].print(1,2,3,4,end="", text_color="red", background_color="yellow")
window["-ML1-"+sg.WRITE_ONLY_KEY].print("\n", end="")
window["-ML1-"+sg.WRITE_ONLY_KEY].print(1,2,3,4, text_color="white", background_color="green")
counter = 0

while True:
    event, values = window.read(timeout=100)
    if event in (None, "Exit"):
        break
    if event == "Go":
        window["-ML1-"+sg.WRITE_ONLY_KEY].print(event, values, text_color="red")
    window["-ML2-"+sg.WRITE_ONLY_KEY].print(counter)
    counter += 1

window.close()


Kodu ve çıktıyı tek tek incelemenizi öneririm. timeout=100 ile hiç bir etkileşim olmasa bile window.read() metodu 100 ms içinde çıkarak o anki değerleri veriyor.


Gelelim 2. tekniğe, eğer yazılmış kodumuzun her print satırını değiştirmek istemiyorsak, ama Multiline elemana yazmasını istiyorsak. Yapmamız gereken print metodunun üzerine yazmak. Yeni tanımlayacağımız bir metodu print olarak Python'a tanıtacağız.

Eğer metod tanımı yapacaksak şöyle:

def mprint(*args, **kwargs):
    window["-ML1-"+sg.WRITE_ONLY_KEY].(*args, **kwargs)

print = mprint

eğer lambda eşitliği olarak tanımlayacaksak böyle

print = lambda *args, **kwargs: window['-ML1-' + sg.WRITE_ONLY_KEY].print(*args, **kwargs)

yaparız. Şimdi programımızı bu şekilde yazalım:

MultilinePrint2.py
import PySimpleGUI as sg

def mprint(*args, **kwargs):
    window["-ML1-"+sg.WRITE_ONLY_KEY].print(*args, **kwargs)

print = mprint

layout = [   [sg.Text("Multiline elemanına yazdırma demosu")],
            [sg.MLine(size=(40, 8), key="-ML1-"+sg.WRITE_ONLY_KEY)],
            [sg.MLine(size=(40, 8), key="-ML2-"+sg.WRITE_ONLY_KEY)],
            [sg.Button("Go"), sg.Button("Exit")]   ]

window = sg.Window("Pencere etiketi", layout, finalize=True)

# olay döngüsüne girmeden bu satırlar işlensin diye finalize çağırdık
print(1,2,3,4,end="", text_color="red", background_color="yellow")
print("\n", end="")
print(1,2,3,4, text_color="white", background_color="green")
counter = 0

# ikinci multiline elemana yazıma geçelim
print = lambda *args, **kwargs: window['-ML2-' + sg.WRITE_ONLY_KEY].print(*args, **kwargs)


while True:
    event, values = window.read(timeout=100)
    if event in (None, "Exit"):
        break
    if event == "Go":
        print(event, values, text_color="red")
    print(counter)
    counter += 1

window.close()

Bu kadar yazdıklarımı bir yayınlayayım. Arkası gelecek, biraz daha karmaşık reçetelerle devam edeceğiz. Tekrar görüşmek ümidiyle kalın sağlıcakla..







1 yorum:

  1. Çok faydalı dersler. Tesekkürü borç bilirim: Teşekkürler

    YanıtlaSil