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

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.

1 Beğeni

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.

1 Beğeni

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

1 Beğeni

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.

1 Beğeni

Şö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.

1 Beğeni

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:

1 Beğeni

@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 = []
1 Beğeni

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()
1 Beğeni

@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.

2 Beğeni

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?

1 Beğeni

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)

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

1 Beğeni

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.

1 Beğeni

Tamam, sizin dediğiniz gibi yaptım, ctrl + f ve ctrl + g tuş kombinasyonlarını kaldırdım, yerine girinti durumuna göre çalışan bir girinti ekleme ve girinti silme işlemi tanımladım. Çalışma şekli PyCharm’daki girintilerin çalışma şekline benziyor.

Programı çalıştırıp Enter ve BackSpace tuşlarının nasıl çalıştığına bir bakın isterseniz.

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

import tkinter as tk

root = tk.Tk()

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

right_click = None


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


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


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)

root.mainloop()

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

1 Beğeni

README dosyasında uygulamayı çalıştırma komutlarını ekledim. Şimdi daha sade oldu. Sıkıntı için kusura bakmayın, doğru paket yapısını bulmaya çalışırken sık sık dizin yapısını değiştirmek durumunda kaldım.

Yok, olmazdı. Uygulamalar büyüdükçe tek dosya içinde geliştirmesi giderek zorlaşıyor. Ki bu uygulamanın büyüme kapasitesi yüksek. Şimdiden önlem alınıp modüler bir şekilde geliştirmeye devam edilmezse, ileride bu baş ağrısına sebep olur. Hatta bu modülerliği kurmak için şimdi harcanan zamandan çok daha fazlası hata ayıklamaya gitmeye başlar.

1 Beğeni

:+1:

Aynen öyle. Girintileme olan yerleri bulmak kolay: Eğer bir satırda boşluk karakterlerinden önce bir kod yoksa, o satırdaki boşluk karakterleri girintilemedir.

1 Beğeni

Klonu yeniden indirdim.
Çalıştırmak için aşağıdakileri yazdım:

python3 -m __init__.py

Maalesef şimdi de şöyle bir hata verdi:

/usr/bin/python3: Error while finding module specification for '__init__.py' (AttributeError: module '__init__' has no attribute '__path__')

Sonra, visual_python dizinin içinde, eadme’de yazdığı çalıştırmayı denedim:

python3 -m visual_python

Ancak aşağıdaki gibi bir hata aldım.

_tkinter.TclError: bitmap "icon.ico" not defined

Neyse, şimdilik app.py içindeki 14. satırı yoruma aldım. Çalıştı.

1 Beğeni
$ python3 -m visual_python

Bu komutu ana dizinde (README.md dosyasının olduğu dizin) vermelisiniz. Çünkü icon.ico dosyası bu dizin içinde.

1 Beğeni

O dizinde komutu çalıştırdım.

1 Beğeni

Windows kullanmıyorsunuz sanırım. GNU/Linux’te .ico dosyaları ile ilgili sorun çıktığını okumuştum. Belki bu konuda da bir şeyler yapabilirsiniz, GNU/Linux bir işletim sistemine erişimim olmadığı için ben pek bir şey yapamıyorum. :smile:

1 Beğeni

O sırada kullanmıyordum evet. :slight_smile:
Yok sorun değil, ilgili satırı yoruma aldım ve çalıştırdım.
Bu arada girinti ekleme işlemini programa ekledim ve PR gönderdim ama sanırım bir hata veriyor.

1 Beğeni