Bir başka kod editörü: Visual Python

metin-düzenleyici
python
visual-python
program-tanıtımları
tkinter

#21

Kendinizi geliştirme konusunda şunu yapabilirsiniz: Sınıflarla alakalı öğrendiğiniz her bilgiyi uygulamaya koyup, kendinizi sınıflara alıştırabilirsiniz.


#22

peki ismailarilik hocanın bu çalışmada en başta oluşturduğu class editor un kısa açıklamasını yapabilirmisiniz ne ne işe yarıyor anlamak için


#23

@ismailarilik hocam kusura bakma konuyu biraz dağıttık.
@ismailarilik hoca bu örnekte sınıfları zorunlu olarak kullanmamış diyebilirim. İnternette karşılaşabileceğiniz, Tkinter ile yapılmış bir çok uygulamada sınıfların yaygın olarak kullanıldığını görebilirsiniz. Pekala sınıfları kullanmadan da aynı programı tasarlayabilirdi @ismailarilik hoca.

Örneği biraz incelemeye çalışalım:

class Editor(tk.Text):
# Burada İsmail Hoca, Editor isimli sınıfı oluştururken sınıfın 
# özelliklerinin tk.Text sınıfından miras alınacağını belirtmiş.
    def __init__(self, master=None, undo=True, wrap=tk.NONE):
    # Burada, Editor sınıfından örnek oluşturulduğunda, örneğin ne gibi
    # özelliklere sahip olacağı argümanlarla belirtilmiş. Gördüğünüz gibi
    # bu argümanların ön-tanımlı değerleri var. 
    # __init__ bir magic function'dır. Magic functionların gösterimi
    # __fonksiyon__ şeklinde olur.
        super().__init__(master, undo=undo, wrap=wrap)
        # Burada,  Editor sınıfından bir tane örnek oluşturulduğu zaman
        # miras alınan sınıfın özelliklerinin örneğe aktarılacağını görüyoruz.
        # Buna kalıtım (inheritance) denir.
        # Ve __init__(self) fonksiyonu içinde, daha başka değişkenler 
        # tanımlanmış.
        # __init__ fonksiyonu içinde tanımlanan değişkenler veya
        # bu fonksiyon altında çağrılan başka fonksiyonlar,
        # Editor sınıfından örnek oluşturulduğu zaman,
        # örneğin sahip olacağı özellikleri ve/veya örnek
        # oluşturulur oluşturulmaz çalışmaya başlayacak
        # fonksiyonları belirtir. 
        # Mesela İsmail Hocam şöyle bir değişken tanımlamış:
        # vertical_scrollbar = tk.Scrollbar(master)
        # Gördüğünüz gibi değişkenin başında self ifadesi yok.
        # Dolayısıyla bu değişken, sınıfın diğer fonksiyonlarında
        # şu haliyle kullanılamaz. Zaten sınıf içine baktığınızda 
        # başka hiç bir yerde kullanılmadıklarını göreceksiniz.

Bazı durumlarda yapmak istediğiniz işlem için sadece sınıf kullanabilirsiniz. Sizinle bu konuyla ilgili
bir örneği Python derleyici yapma başlığında paylaşmıştım.

Ama genellikle bir değişkene erişimi kolaylaştırmak ve ayrıca kodlara bir düzen vermek için de kullanılır (Yani diyelim kedi isminde bir nesneniz var ama bu nesnenin yaş, renk, boy, miyavlama gibi bir çok özelliği olduğunu varsayalım. Böyle nesneler için sınıf kullanabilirsiniz. Kullanmasanız da olur ancak kullanırsanız belli bir nesnenin özelliklerini bir arada tutmuş olursunuz.).

self ifadesi ile, __init__ içinde tanımlanmış her bir değişken, sınıfın herhangi bir yerinde kullanılabilir. Oysa biz sınıf yerine fonksiyon kullandığımızda ve bir fonksiyon içindeki değişkene başka bir fonksiyon içinden erişmek istediğimizde global deyimini kullanırız. Hatırlayın, bununla ilgili SOCKET programlamada fonksiyon içindeki DEĞİŞKENİ BAŞKA BİR FONKSİYONDA TANIMLAMA ! başlığında sizinle yazışmıştık.

Sınıf Ve Fonksiyon Arasındaki Fark Nedir? başlığını da daha önce incelemediyseniz, bir göz atın derim.

Sınıfların, fonksiyonlara göre şöyle bir özelliği de var; bir sınıfın içinde tanımlanmış niteliklere ve fonksiyonlara dışarıdan erişmek mümkünken, fonksiyonda bu ancak return ve yield deyimleri kullanılırsa mümkündür. Örnek üzerinden anlatmaya çalışayım:

def f():
    x = 1
    y = 2
    return x, y
    # Burada return'ı kullanmasaydık, x ve y'ye
    # dışarıdan erişemezdik.


x1 = f()[0]
y1 = f()[1]
print(x1, y1)

Şimdi de yukarıdaki fonksiyona benzer bir sınıf oluşturalım:

class C:
    x = 1  # Sınıf niteliği
    y = 2  # Sınıf niteliği


x1 = C.x
y1 = C.y
print(x1, y1)

# Sınıf niteliklerine hem sınıftan (C) hem de örnekten (C()) ulaşabiliriz.

x2 = C().x
y2 = C().y
print(x2, y2)

# Mesela bu sınıfın __init__() isimli bir metodu yok:
print(C().__init__())
# Sonuç: None

Yani __init__'de tıpkı diğer fonksiyonlar gibi bir fonksiyondur. Ancak bu fonksiyonda, bir örnek oluşturulur oluşturulmaz otomatik olarak yapılacak işlemler tanımlanır.
Mesela;

class C:
    def __init__(self):
        print("Örnek oluşturulduğunda bu mesaj otomatik olarak ekrana yazdırılır.")


c = C()
# Sonuç: Örnek oluşturulduğunda bu mesaj otomatik olarak ekrana yazdırılır.

Diğer bütün fonksiyonlar ise çağrılırlarsa çalışırlar.
Aynı örnekten devam edeyim:

class C:
    def __init__(self):
        print("Örnek oluşturulduğunda bu mesaj otomatik olarak ekrana yazdırılır.")
        self.x = 10  # Örnek niteliği.

    def baska_bir_fonksiyon(self):
        """Bu fonksiyon da örnek niteliğinin karesini alsın."""
        return self.x ** 2

    
c = C()
# Sonuç: Örnek oluşturulduğunda bu mesaj otomatik olarak ekrana yazdırılır.

# Diğer fonksiyonu ise şu şekilde çalıştırırız.
degisken = c.baska_bir_fonksiyon()
print(degisken)

Forumda sınıflarla alakalı bazı başlıklar var onları da okumanızı ve ayrıca istihza belgelerine de bakmanızı tavsiye ederim.


#24

çok teşekkür ederim çok emek harcadınız elinize sağlık


#25

Rica ederim, umarım yardımcı olabilmişimdir.


#26

çok faydası oldu tşkkrler şimdi daha detaylı olarak sınıfların miras almasını inceliyorum bu konuyla alakalı


#27

Uygulamanın adı değişti, Visual Python oldu. Yeni GitHub veri havuzu adresi de şurası: https://github.com/ismailarilik/visual-python

Birkaç yenilik var:

  • Kodlar nesneye yönelik programlama usullerine göre düzenlendi.
  • Renklendirme geliştirildi.
  • Birkaç küçük geliştirme ve hata düzeltme yapıldı.

Renklendirme için kullanılan renkleri özenle seçtiğime emin olabilirsiniz. Hem kolay görünen hem de birbirinden yeterince farklı renklerin sayısı gerçekten az. Önerileriniz varsa duymak isterim.

Uygulamaya katkıda bulunmak isteyenleri de beklerim.


#28

Uygulama için bulma komutunu(ctrl+f) ve değiştirme komutunu(ctrl+h) yazmaya başlamıştım bir hafta kadar önce. Bu sırada şunu farkettim:Acilen girintileme sorunu çözülmeli.


#29

Değiştirme komutu hangi editörde ctrl-h olarak tanımlı? Ya da genelde bu kısayola mı atanır? Bilmediğimden soruyorum; bu kısayolu kullanmadım hiç. Her şekilde varsayılan klavye kısayolları için tartışılması gerek; çoğunluğun aşina olacağı kısayollar olmalı. Tabii değiştirme olanağı da sağlanmalı. Siz yaptığınız değişiklikleri ilgili veri havuzuna çekme isteği olarak gönderin, orada tartışırız.

Eğer uygulamayı, uygulamayı geliştirmek için kullanıyorsanız katılıyorum. Değilse; arama, farklı kaydetme, değişikliği başlıkta yıldız ile bildirme, çıkışta kaydedilmemiş dosya için uyarı gösterme, vb. daha öncelikli konular var.


#30

Bunlar IDLE ve vs code de ortak özellikler. Vs code de bu kısayollar değiştirilebiliyor.
Kısayollar için bir pr yollayım size.

Şu an uygulama geliştirilme aşamasında olduğu için diğer sorunlar çok da dikkat çekmiyor. Ancak python dilinde girintileme olmazsa olmaz. Kıvrandırıyor. Acı çektiriyor.


#31

Acaba girintileme için aşağıdaki kodlar işinize yarar mı?

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

import time
import tkinter as tk

root = tk.Tk()

text = tk.Text(master=root)
text.pack()

# Kısayol kombinasyonlarını kontrol etmek için bir liste tanımlanır.
shortcuts = []
# ctrl tuşuna basınca başlayan zaman sayacı
start = 0


def indent(event):
    global shortcuts, start
    # İmlecin satır değeri bir değişkene atanır.
    row = text.index("insert").split(".")[0]
    # İmleçten bir önceki satır ile imlecin bulunduğu satır
    # arasında kalan karakter dizileri kaydedilir.
    line = text.get("{}.0".format(int(row) - 1), "{}.0".format(row))
    # Eğer kullanıcı "enter" tuşuna basarsa:
    if event.keysym == "Return":
        # Eğer bir önceki satır ":" işaretiyle bitmişse:
        if line[-2] == ":":
            # Bir önceki satırdaki girintelemeyi say
            count = line.count(" " * 4)
            # Girintileme sayısının bir fazlası kadarını girintiyi ekle.
            # (Girintisi 0 olan bir satırda ':' işareti kullandıktan sonra,
            # bir sonraki satırda tek bir girinti eklemek için 
            # count'ı 1 ile toplamamız gerek.)
            text.insert("insert", " " * 4 * (count + 1))
    elif event.keysym == "Control_L":  # Eğer kullanıcı sol ctrl tuşuna basarsa:
        # sayacı çalıştır.
        start = time.time()
        # Eğer "Control_L" shortcuts listesinde değilse,
        if event.keysym not in shortcuts:
            # shortcuts listesine ekle.
            shortcuts.append(event.keysym)
    # Eğer kullanıcı "f" tuşuna basarsa:
    elif event.keysym == "f":
        # Eğer "Control_L" shortcuts listesindeyse:
        if "Control_L" in shortcuts:
            # Eğer geçen zaman 0.5 saniyeden azsa:
            if time.time() - start < 0.5:
                # Bir önceki satırdaki girintilemeyi say
                count = line.count(" " * 4)
                # Ve girinti sayısı kadar girintiyi widgete ekle.
                text.insert("insert", " " * 4 * count)
                # Sayacı sıfırla.
                start = 0
                # Listeyi sıfırla.
                shortcuts = []


text.bind("<KeyRelease>", indent)
root.mainloop()

2018-07-20%2003-20-24%20ekran%20g%C3%B6r%C3%BCnt%C3%BCs%C3%BC


#32

Teşekkürler. Evet, işe yarayabilir. Yine de kodla ne kadar entegre olabileceğini görmek için bir çekme isteği göndermenizi tavsiye ederim.

Bu arada "Control_L" ve "f" tuşlarının buradaki anlamlarını anlayamadım.


#33

Şöyle anlatmaya çalışayım hocam: Ekran görüntüsünde gördüğünüz gibi, bir satır eğer “:” karakteriyle bitmişse ve kullanıcı enter tuşuna basarsa bir sonraki satırda bir girinti oluşturulur.

# Diyelim şöyle bir şey yazdık
def f():
    def g():  # Bu satırın girintisi otomatik olarak oluşturulur.
        def h():  # Bu satırın girintisi otomatik olarak oluşturulur.
            # Bu satırın girintisi otomatik olarak oluşturulur.
# Ama bir alt satır olan bu satırı bir üst satırdaki girintiden başlatmak
# için ctrl + f tuşuna basarız. Ve aşağıdaki gibi bir sonucu hiç 
# space tuşuna basmadan hızlıca elde ederiz.
            # Bunun gibi.

Özetle ctrl + f özelliği biraz tab tuşunun yaptığına benziyor. Ancak tab gibi 8 tane boşluk koymak yerine, bir üst satırdaki girintiyi hesaplıyor. Alt satıra o girintiyi ekliyor.

Bir de şöyle bir özellik eklenebilir. Öyle bir tuş kombinasyonu tanımlanır ki, bir adet girinti silinir. Girinti ekleme işleminin tersi bir işlem gibi düşünün. Yani her zaman kaldığımız satırdaki girintiden devam etmeyebiliriz değil mi? Şunun gibi:

def f():
    def g():
        if a:
            ...
        else b: # İşte bu satırı tekrar if'in girintisine tuş kombinasyonu ile getirebiliriz.
        # ctrl + f'e basınca if blokunun altındaki girinti alt satıra eklenecek.
        # Ama sonra başka bir özel tuş kombinasyonu ile else'in girintisi 1 kademe düşürülebilir.
        # Böylece girintiyi ayarlamak için backspace tuşuna basmak zorunda kalmayız.

Not: Bu arada “Control_L”, sol ctrl tuşu demek, Control_Left’in kısaltılmışı.

Tamam entegre etmeye çalışacağım.


#34

Binding ile bu kombinasyonu atayabilirdiniz:
wigdet.bind("<Control-KeyPress-f>",indent)
Yanlış hatırlamıyorsam böyle bir şeydi.
Şu linkteki editörde kullanılıyor:


#35

@hasser, o da olur, bence fark etmez.
Başka ctrl tuş kombinasyoları oluşturabiliriz, örneğin aşağıdaki gibi.
Yukarıda bahsettiğim 1 adet girintiyi (4 adet boşluğu) silmeye yarayan bir koşul mesela:
Bu da ctrl + g tuşuna basınca aktif olacak koşul.

    elif event.keysym == "g":
        # Eğer "Control_L" shortcuts listesindeyse:
        if "Control_L" in shortcuts:
            # Eğer geçen zaman 0.5 saniyeden azsa:
            if time.time() - start < 0.5:
                text.delete("{}.{}".format(row, int(col) - 4), "{}.{}".format(row, col))
                # Sayacı sıfırla.
                start = 0
                # Listeyi sıfırla.
                shortcuts = []

#36

Düşündüm de @hasser’in bahsettiği daha şık olur sanki.

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

import tkinter as tk

root = tk.Tk()

text = tk.Text(master=root)
text.pack()


def indent(event):
    row, col = text.index("insert").split(".")
    line = text.get("{}.0".format(int(row) - 1), "{}.0".format(row))
    if event.keysym == "Return":
        if line[-2] == ":":
            count = line.count(" " * 4)
            text.insert("insert", " " * 4 * (count + 1))


def ctrl_f(event):
    row, col = text.index("insert").split(".")
    line = text.get("{}.0".format(int(row) - 1), "{}.0".format(row))
    count = line.count(" " * 4)
    text.insert("insert", " " * 4 * count)


def ctrl_g(event):
    row, col = text.index("insert").split(".")
    text.delete("{}.{}".format(row, int(col) - 4), "{}.{}".format(row, col))


text.bind("<KeyRelease>", indent)
text.bind("<Control-KeyPress-f>", ctrl_f)
text.bind("<Control-KeyPress-g>", ctrl_g)

root.mainloop()

#37

@ismailarilik hocam, clone’u indirdim ve __init__.py dosyasını çalıştırmayı denedim, aşağıdaki hatayı verdi.

ModuleNotFoundError: No module named '__main__.app'; '__main__' is not a package

Keşke kodları bu kadar parçalamasaydınız hocam, tek bir script üzerinden ilerlesek daha mı rahat olurdu acaba diye düşünüyorum.


#38

Ctrl+F, genelde arama kutusunu açmak için kullanılan bir kısayol. Sanırım siz “Format” anlamında kullanmışsınız. Tabii ki önemli bir durum değil, farklı bir kısayol bulunabilir.

Hem Ctrl+F, hem de bunun için şunu diyebilirim: Kullanıcı, kısayolları, olabildiğince, bilmek zorunda kalmamalı. Ki bilmek ayrı, uygulamak ayrıdır. Kullanıcıların, çok bilindik olmayan kısayollara alışmak için çaba harcayacaklarını sanmam. Bir de buradaki durum için şöyle bir şey var: Genelde sekmeler bir dosya içindeki sekmeler 3-5 sekmeyi geçmez. Bu durumda kullanıcı genelde en fazla 2-3 kez Backspace tuşuna basarak istediğini elde edebilir. Bir kısayol tanımlasak bu kısayolun kendisi zaten 2 kez basmaya denk gelecek. Yani bana göre çok iyi bir kazanımı yok burada kısayolun.

“Control_L” yerine “Control” kullanmak daha iyi değil mi her yerde? Yani sol ve sağdaki Ctrl tuşları ile bir başka aynı tuşun kombinasyonuna farklı bir görev verildiği bir durum olabilir mi?


#39

Tkinter’da olayların(event) bağlanması(bind) ile ilgili yeni keşfettiğim bir şey var. Örneğin şöyle bir kodumuz var diyelim:

text.bind("<Control-KeyPress-f>", ctrl_f)

Bu durumda Caps Lock açıkken Ctrl+F’ye bastığınızda hiçbir şey olmaz. Çünkü siz Ctrl+f’yi bağladınız, Ctrl+F’yi değil! Biliyorum, şaşırtıcı ama Tkinter’da durum tam olarak bu. Çözümü ise basit:

text.bind("<Control-KeyPress-f>", ctrl_f)
text.bind("<Control-KeyPress-F>", ctrl_f)

@dilde_olupbiten, keysym kullanarak yazdığın kodlarda böyle bir şeye denk gelmiş miydin? Aynı şey orada da var mı, merak ettim.


#40

Evet, katılıyorum, önemli bir durum değil, farklı bir kısayol bulunabilir.

O zaman bunu bir düzenlemeye çalışayım. ctrl + f’e gerek kalmadan, “enter” tuşuna basıldığında, alttaki satır, üstteki satırın girintisine otomatik olarak gelsin.
Yani ctrl_f fonksiyonunu kaldırıp indent fonksiyonunu şu şekilde yazabiliriz (tıpkı PyCharm’daki gibi.):

def indent(event):
    row, col = text.index("insert").split(".")
    line = text.get("{}.0".format(int(row) - 1), "{}.0".format(row))
    if event.keysym == "Return":
        count = line.count(" " * 4)
        if line[-2] == ":":
            text.insert("insert", " " * 4 * (count + 1))
        else:
            text.insert("insert", " " * 4 * count)

Evet keysym için de benzer bir yol izleyebiliriz. Ama aşağıdaki kodlarda, keysym sadece bir yerde kullanıldı, “Enter” tuşuna basıldığında yapılacak olan işlemde.

Bu arada ctrl + f’in yerine yeni bir girinti tanıma işlemi için ve caps lock durumu için bir düzenleme yaptım.
Kodları şöyle değiştirdim:

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

import tkinter as tk

root = tk.Tk()

text = tk.Text(master=root)
text.pack()


def indent(event):
    row, col = text.index("insert").split(".")
    line = text.get("{}.0".format(int(row) - 1), "{}.0".format(row))
    if event.keysym == "Return":
        count = line.count(" " * 4)
        if line[-2] == ":":
            text.insert("insert", " " * 4 * (count + 1))
        else:
            text.insert("insert", " " * 4 * count)


"""def ctrl_f(event):
    row, col = text.index("insert").split(".")
    line = text.get("{}.0".format(int(row) - 1), "{}.0".format(row))
    count = line.count(" " * 4)
    text.insert("insert", " " * 4 * count)"""


def ctrl_g(event):
    row, col = text.index("insert").split(".")
    text.delete("{}.{}".format(row, int(col) - 4), "{}.{}".format(row, col))


def ctrl_a(event):
    text.tag_add("sel", "1.0", "end")
    text.mark_set("insert", "1.0")
    text.see("insert")
    return "break"


right_click = None


def cancel_popup(event):
    global right_click
    if right_click is not None:
        right_click.destroy()


def popup(event):
    global right_click
    cancel_popup(event)
    right_click = tk.Menu(master=None, tearoff=False)
    right_click.add_command(label="Kopyala", command=lambda: root.focus_get().event_generate('<<Copy>>'))
    right_click.add_command(label="Yapıştır", command=lambda: root.focus_get().event_generate('<<Paste>>'))
    right_click.add_command(label="Sil", command=lambda: root.focus_get().event_generate('<<Clear>>'))
    right_click.add_command(label="Kes", command=lambda: root.focus_get().event_generate('<<Cut>>'))
    right_click.add_command(label="Tümünü Seç", command=lambda: root.focus_get().event_generate('<<SelectAll>>'))
    right_click.post(event.x_root, event.y_root)


text.bind("<Button-1>", cancel_popup)
text.bind("<Button-3>", popup)
text.bind("<KeyRelease>", indent)
text.bind("<Control-Key-A>", ctrl_a)
text.bind("<Control-Key-a>", ctrl_a)
# text.bind("<Control-KeyPress-f>", ctrl_f)
# text.bind("<Control-KeyPress-F>", ctrl_f)
text.bind("<Control-KeyPress-g>", ctrl_g)
text.bind("<Control-KeyPress-G>", ctrl_g)
root.mainloop()

Şöyle durumlar için bu özelliği ekledim:

class A:
    def __init__(self):
        self. x = None
        for i in range(10):
            print("gjkgjkgj")
# Şimdi bu satırdayken, ctrl + f'e bassak;
# (Veya yeni düzenlemeye göre print("gjkgjkgj") fonksiyonundan sonra
# sadece enter tuşuna bassak)
            # İmleç bu sütun konumuna otomatik olarak gelir.
    # Ama diyelim bu yorumun başladığı sütuna gelmek istiyorum.
    12345678
    # Toplam 8 tane boşluk var. İşte bu 8 boşluğu
    # ctrl + g'ye iki kere basarak silebiliriz.
    # Öbür türlü 8 kere silme tuşuna basmak gerekecek.
    # Üstelik boşluk sayısı 8 değil de 12 veya 16 da olabilir.
    # Mesela alt alta yazılmış for döngülerini olduğunu düşünelim.
    # Her bir for blokunun içinde if durumları olabilir.

Veya bu son bahsettiğim ile ilgili şöyle bir şey de yapılabilir herhalde, backspace tuşuna basıldığında, şayet soldaki karakter girintiden oluşuyorsa, 4 adet boşluk silinebilir, şayet soldaki karakter normal bir karakter ise tek bir karakter silme işlemi tanımlanabilir. Tıpkı PyCharm’daki gibi, bir denemek lazım.