Tkinter/Pygments: Söz Dizimi Renklendirmesi

pygments

#16

Yok denemedim, hemen deniyorum.


#17

Şöyle bir hata verdi:

_tkinter.TclError: bad event type or keysym "Modified"

Sanırım şöyle olmalı:

text.bind("<<Modified>>", search)

Edit: İlginçtir widgete yazı yazarken yazılar renklendirilmiyor ama bir scriptteki kodları widgete aktarınca yazılar renklendiriliyor ama bu sefer de widgete daha sonradan eklemek istediğimiz yazılar renklendirilmiyor.


#18

Bu yavaşlık sorunuyla başka bir editörde karşılaşmıştım. Renklendirme işi, doğası gereği, yavaş çalışmaya meyilli bir iş. Belki her karakter girişinde renklendirme yapmak yerine sadece değiştirilen satırda renklendirme yapılabilir. Bunun dezavantajı olur mu bilemem, olursa da ele alınması gerek.


#19

Acaba bu değiştirilen satırda renklendirme işlemi nasıl yapılır biraz çalışayım dedim de, garip bir davranışla karşılaştım.

Kodlar:

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

import sys
if sys.version_info.major == 2:
    exit()
elif sys.version_info.major == 3:
    import io
    import keyword
    import builtins
    import tokenize
    import threading
    import tkinter as tk


root = tk.Tk()
text = tk.Text(master=root, fg="white", bg="black", font="TkDefaultFont 10")
text.pack(fill="both", expand=True)

count = 0


def colorize(*args):
    global count
    row1, col1 = args[0].start
    start = str(row1) + "." + str(col1)
    row2, col2 = args[0].end
    end = str(row2) + "." + str(col2)
    text.tag_add(str(count), start, end)
    try:
        text.tag_config(str(count), foreground=args[1], font=args[2])
    except IndexError:
        text.tag_config(str(count), foreground=args[1])
    count += 1


def search(event):
    index = text.index("insert")
    try:
        for i in tokenize.tokenize(io.BytesIO(text.get("{}.0".format(index[0]), "end").encode("utf-8")).readline):
            if i.type == 1:
                if i.string in keyword.kwlist:
                    colorize(i, "orange")
                elif i.string in dir(builtins):
                    colorize(i, "blue")
                else:
                    colorize(i, "white")
            elif i.type == 2:
                colorize(i, "cyan")
            elif i.type == 3:
                colorize(i, "purple")
            elif i.type == 53:
                if i.string == "," or i.string == "." or i.string == ":":
                    colorize(i, "orange")
                elif i.string == "(" or i.string == ")" or i.string == "[" \
                        or i.string == "]" or i.string == "{" or i.string == "}":
                    colorize(i, "darkred")
                else:
                    colorize(i, "green")
            elif i.type == 57:
                colorize(i, "grey", "TkDefaultFont 10 italic")
    except tokenize.TokenError:
        pass


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

Bu kodlarda şöyle bir kısım var:

index = text.index("insert")
    try:
        for i in tokenize.tokenize(io.BytesIO(text.get("{}.0".format(index[0]), "end").encode("utf-8")).readline)

Burada yapmak istediğim şey şuydu: kullanıcı alt satıra geçtiği zaman, text.get() fonksiyonunun satır parametresi de değişsin, böylece “1.0” ile “end” arasında değil de “2.0” ile “end” arasında veya “3.0” ile “end” arasında bir arama gerçekleştirilsin.

Yalnız aşağıdaki gibi bir sonuç aldım. Bu arada event kısmında "<KeyRelease>" var, yine tuş basımında etkili olan bir işlem bu. "<<Modified>>" yaptığımda ise hiç renklenme olmuyor.

2018-06-14%2017-12-50%20ekran%20g%C3%B6r%C3%BCnt%C3%BCs%C3%BC


#20

Şurada birisi başarmış hızlandırmayı.


#21

Teşekkür ederim, bu başlığa bakmıştım ama üzerinde çalıştığım örneğe uyarlayamamıştım. Tam anlayamadım sanırım. Bir daha bakarım.


#22

Hocam ben bir şeyler anladım ama onu muhtemelen siz de anlamışsınızdır.

Adamın dedigine gore adam @ismailarilik beyin dedigi gibi butun metni kontrol edecegime kullanıcının yazdıgı satırı kontrol etmemiz ve renklendirmeyi ona göre yapmamız lazımmış.

Yapabildigim kadarıyla kodlarda yorum olarak açıklama yapmaya çalıştım.

def deafultHighlight(self, argument):
    self.content = self.text.get("1.0", tk.END) #metin alınır
    self.lines = self.content.split("\n") #metin satırlara bölünür

    if (self.previousContent != self.content): #eger onceki metinle şimdiki metin aynı degilse(yani metin degişmişse)
        self.text.mark_set("range_start", self.row + ".0")
        data = self.text.get(self.row + ".0", self.row + "." + str(len(self.lines[int(self.row) - 1])))  #kullanıcının bulundugu satır alınır

        for token, content in lex(data, PythonLexer()): #tokenlere ayırma
            self.text.mark_set("range_end", "range_start + %dc" % len(content))
            self.text.tag_add(str(token), "range_start", "range_end") #renklendirmeyi yapıyor sanırsam burada
            self.text.mark_set("range_start", "range_end")

    self.previousContent = self.text.get("1.0", tk.END) #onceki metni yeni boyanan metin yapıyor ki sorun olmasın sonra

Tek anlamadıgım nokta şu:
self.text.mark_set() metodunu niye kullanmış bu adam.

mark_set metodunun işlevi araştırılmalı,ben de anlayamadım ne işlevi oldugunu.

Bu arada herkesin bayramı kutlu olsun :slight_smile:
Kolay gelsin :slight_smile:


#23

Merhaba, paylaştığınız kodun, bütün kodlar içerisinde nasıl işlev gördüğünü pek anlayamadım. Mesela bu fonksiyon ne zaman çağrılır? Fonksiyonun argument parametresi var ama fonksiyon içerisinde kullanılmamış, nerede ihtiyaç duyuluyor, text.tag_configure() ne zaman nerede çağrılıyor gibi sorular fonksiyonu anlamamı zorlaştırdı. Bir de ben de mark_set()'in kullanımına pek aşina değilim.

İyi bayramlar.


#24

Aslında kodun tamamı görülmeden bir şey söylenemez,ama belki de bir class oluşturulmuş ve Text sınıfını miras almıştır.

Eger defaultHighlight metodu Text widgetine aitse fonksiyonun hata vermemesi için koyulmuş olabilir belki.Bilemiyorum.

Galiba text.tag_configure yerine text.tag_add benzer bir işlevi yerine getiriyor olabilir,alttaki yorumlara soyle bir bakınca bunu söyleyebiliyorum.Alttaki yorumlar bize biraz da olsa yol gosterir bence.Zor şartlar altinda yazmaktayim.Musait olunca daha da yazabilirsem yazacagım.

Takipteyim.

Kolay gelsin :slight_smile:


#25

Evet bir class oluşturulmuş ama neye benziyor onu bilmiyoruz.

Belki de event’in yerine kullanılıyordur?

tag_add(tag_adı, index1, index2) şeklinde bir fonksiyonken, tag_configure(tag_adı, stil_argümanları) şeklinde bir fonksiyon. Yani birbirlerinin yerine kullanılamazlar. İlki, özel bir adla belirtilmiş bir stilin hangi karakter aralığında geçerli olacağını, ikincisi ise bu özel adla belirtilmiş stilin özelliklerinin ne olacağını belirtiyor.

Alttaki yorumlar evet yardımcı olabiliyor ama ne yalan söyleyeyim, o mesajın altındaki yorumların pek yardımı olmadı.

Neyse daha sonra yöntem değiştirmeye çalışırım. Şimdiye kadar iki tane farklı yolla renklendirme işlemini denedim. İlkinde Pygments kullanılmıştı ve istediğim sonucu elde edememiştim. İkincisinde tokenizer modülü kullanıldı, onda istenilen sonuç elde edildi ama bu sefer de widgetin içeriği arttıkça renklendirme işleminin gecikmesi sorunu ortaya çıktı.

Belki arkadaşın o başlıkta denediği yöntem, denediğim iki yöntemden de farklıdır ve benim daha önce izlediğim yöntemlere uyarlanması zordur. Veya uyarlanabilmesi için illa bir değişiklik yapılması gerekiyor olabilir. Arkadaşın kodlarının tamamını görebilsem, anlamam daha rahat olabilirdi ama kodların yarısı ortalıkta olmayınca, anlamam biraz zorlaşıyor.

Bir de, halen sorunu daha önce izlediğim yöntemlere göre çözme eğilimi taşıyorum, ki belki bu sayede daha işlevsel olacak olan başka bir yöntemi kaçırıyorum, bilemiyorum ama sanki daha farklı bir yaklaşım geliştirmek gerekiyor diye düşünüyorum. Neyse zamanla umarım belli olur.

Tekrar teşekkür ederim.


#26

Son olarak şöyle bir yol izledim:

Program sürekli imlecin olduğu satırı renklendirecek. Böylelikle daha önce renklendirilmiş satırlar tekrar renklendirilmeyecek ve programda donma sorunu oluşmayacak.

Ancak bu yöntemin de şöyle bir dezavantajı var:

Eğer kullanıcı widget’in içine başka bir yerden kopyaladığı python kodlarını yapıştırırsa, işte o zaman bu Python kodlarının sadece son satırı renklendirilir. Çünkü imleç Python kodlarının son satırına gelmiş olur.

Eğer kullanıcı yukarı ok tuşuyla imleci bir önceki satıra götürürse, bu sefer bir önceki satır da renklendirilir. Yani widgete dışarıdan python kodları yapıştırıldıktan sonra eğer imleci yukarı ok tuşu yardımıyla ilk satıra kadar götürürsek bütün kodlar renklendirilir. Özetle daha tam istediğim gibi çalıştıramadım programı.

Kodların Son Hali:

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

import sys
if sys.version_info.major == 2:
    exit()
elif sys.version_info.major == 3:
    import io
    import keyword
    import builtins
    import tokenize
    import tkinter as tk


root = tk.Tk()
text = tk.Text(master=root, fg="white", bg="black", font="TkDefaultFont 10")
text.pack(fill="both", expand=True)

count = 0


def colorize(*args):
    global count
    row = text.index("insert").split(".")[0]
    col1 = args[0].start[-1]
    start = row + "." + str(col1)
    col2 = args[0].end[-1]
    end = row + "." + str(col2)
    text.tag_add(str(count), start, end)
    text.tag_config(str(count), foreground=args[1])
    count += 1


def search(event):
    row = text.index("insert").split(".")[0]
    last_line = text.get("{}.0".format(row), "{}.0".format(int(row) + 1))
    try:
        for i in tokenize.tokenize(io.BytesIO(
                last_line.encode("utf-8")).readline):
            if i.type == 1:
                if i.string in keyword.kwlist:
                    colorize(i, "orange")
                elif i.string in dir(builtins):
                    colorize(i, "blue")
                else:
                    colorize(i, "white")
            elif i.type == 2:
                colorize(i, "cyan")
            elif i.type == 3:
                colorize(i, "purple")
            elif i.type == 53:
                if i.string == "," or i.string == "." or i.string == ":":
                    colorize(i, "orange")
                elif i.string == "(" or i.string == ")" or i.string == "[" \
                        or i.string == "]" or i.string == "{" or i.string == "}":
                    colorize(i, "darkred")
                else:
                    colorize(i, "green")
            elif i.type == 57:
                colorize(i, "grey")
    except tokenize.TokenError:
        pass


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

#27

Yavaşlama sorunu nihayet çözülmüştür. İki ayrı işleme göre renklendirme işleminin yapılmasına karar verdim. Kullanıcı CTRL+V’ye basarsa başka bir işlem, normal bir şekilde kodları yazarsa başka bir işlem yapılacak. Yardımcı olan arkadaşlara çok teşekkür ederim. Kodlar aşağıdadır.

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

import sys
if sys.version_info.major == 2:
    exit()
elif sys.version_info.major == 3:
    import io
    import keyword
    import builtins
    import tokenize
    import tkinter as tk


root = tk.Tk()
text = tk.Text(master=root, fg="white", bg="black", font="TkDefaultFont 10")
text.pack(fill="both", expand=True)

count = 0


def colorize(*args):
    global count
    row = text.index("insert").split(".")[0]
    col1 = args[0].start[-1]
    start = row + "." + str(col1)
    col2 = args[0].end[-1]
    end = row + "." + str(col2)
    text.tag_add(str(count), start, end)
    try:
        text.tag_config(str(count), foreground=args[1], font=args[2])
    except IndexError:
        text.tag_config(str(count), foreground=args[1])
    count += 1


def parser_1(*args):
    try:
        for i in tokenize.tokenize(io.BytesIO(
                args[0].encode("utf-8")).readline):
            if i.type == 1:
                if i.string in keyword.kwlist:
                    colorize(i, "orange")
                elif i.string in dir(builtins):
                    colorize(i, "blue")
                else:
                    colorize(i, "white")
            elif i.type == 2:
                colorize(i, "cyan")
            elif i.type == 3:
                colorize(i, "purple")
            elif i.type == 53:
                if i.string == "," or i.string == "." or i.string == ":":
                    colorize(i, "orange")
                elif i.string == "(" or i.string == ")" or i.string == "[" \
                        or i.string == "]" or i.string == "{" or i.string == "}":
                    colorize(i, "darkred")
                else:
                    colorize(i, "green")
            elif i.type == 57:
                colorize(i, "grey", "TkDefaultFont 10 italic")
    except tokenize.TokenError:
        pass


keysym = set()


def parser_2(*args):
    global keysym
    keysym = set()
    for i in range(int(args[0])):
        text.mark_set("insert", "{}.{}".format(i + 1, args[1]))
        parser_1(text.get("{}.0".format(i + 1), "{}.0".format(i + 2)))
        text.mark_set("insert", "{}.{}".format(args[0], args[1]))


def select_parser(event):
    row, col = text.index("insert").split(".")
    if event.keysym == "Control_L":
        keysym.add(event.keysym)
    elif event.keysym == "v" or event.keysym == "V":
        keysym.add(event.keysym)
        if "Control_L" in keysym:
            parser_2(row, col)
    elif event.keysym == "Control_R":
        keysym.add(event.keysym)
        if "v" in keysym or "V" in keysym:
            parser_2(row, col)
    else:
        parser_1(text.get("{}.0".format(row), "{}.0".format(int(row) + 1)))


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

#28

Gerçekten tebrik ederim.Çalışmalarınız bize ve digerlerine yol gösterebilir.

Mesela mark_set in ne işe yaradıgı anlatılabilir :slight_smile:

Kolay gelsin :slight_smile:


#29

mark_set(); ekleme konumunu ayarlayan bir fonksiyon. Bu fonksiyon, argümanlarıyla birlikte aşağıdaki gibi çağrılıyor.

widget.mark_set("işaret ismi", "satır_no.sütun_no")

Belirtilen satır ve sütuna göre karakter ekleme yerini değiştirmeye imkan veriyor.

Şimdi bir önceki mesajımda mark_set() fonksiyonunun bulunduğu kısma bir bakalım.

def parser_2(*args):
    global keysym
    keysym = set()
    for i in range(int(args[0])):
        text.mark_set("insert", "{}.{}".format(i + 1, args[1]))
        parser_1(text.get("{}.0".format(i + 1), "{}.0".format(i + 2)))
        text.mark_set("insert", "{}.{}".format(args[0], args[1]))
  • Öncelikle, bu çalışmada iki tane ayrıştırma yöntemi olduğunu göreceksiniz. Yukarıdaki bunlardan ikincisi. Copy/Paste yaptığımızda çalışan fonksiyon bu.

  • Bu fonksiyona göre, toplam satır sayısı kadar (for i in range(int(args[0]))) satır ziyaret edilir, karakter ekleme yeri her bir satır için değiştirilir (text.mark_set("insert", "{}.{}".format(i + 1, args[1]))).

  • args[1] karakter eklemekle değişen güncel sütun değeri (col), buna karışmıyoruz. Çünkü dışarıdan widgete metin kopyaladıktan sonra karakter ekleme yeri neresi olursa, oradan karakter eklemeye devam edelim istiyoruz.

  • keysym değişkeninin ne işe yaradığı aşağıda anlatılacaktır.

  • mark_set() fonksiyonunun satır değerine ise i + 1 yazıldı. Çünkü for döngüsü dizinlemeye 0’dan başlar oysa text widgetindeki satırların dizinlenmesi 1’den başlar. Bu yüzden satır değeri i + 1.

  • Sonra birinci ayrıştırma yöntemini çağırıyoruz.

parser_1(text.get("{}.0".format(i + 1), "{}.0".format(i + 2)))
  • Widgette bakacağımız yer 1.0 ile 2.0 veya 2.0 ile 3.0 gibi tek bir satıra karşılık geliyor. For döngüsü boyunca, son satıra kadar bütün tek satırlar ziyaret edilecek ve her bir tek satır için parser_1() fonksiyonu; parser_2() fonksiyonunun içindeki değişkenleri argüman alarak çağrılacak…

  • Son olarak, her döngü başa sardığında karakter ekleme yeri, text.index("insert")'den gelen row* ve col durumuna geri gelsin.

 text.mark_set("insert", "{}.{}".format(args[0], args[1]))

Aynı zamanda colorize() fonksiyonu da aşağıdaki gibi değiştirildi:

def colorize(*args):
    global count
    row = text.index("insert").split(".")[0]
    col1 = args[0].start[-1]
    start = row + "." + str(col1)
    col2 = args[0].end[-1]
    end = row + "." + str(col2)
    text.tag_add(str(count), start, end)
    try:
        text.tag_config(str(count), foreground=args[1], font=args[2])
    except IndexError:
        text.tag_config(str(count), foreground=args[1])
    count += 1
  • Burada değiştirilen sadece row isimli değişkenin değeridir. Daha önceki uygulamalarda kullanılan değer args[0].start[0]. Ama bu değer yerine text.index("insert").split(".")[0] değerini kullanacağım: Ayıklama işlemini son satıra çekerek her defasında incelenen satır sayısının az kalmasını sağlayıp programın yorulmaması lazım.

Her tuşa basıldığında hangi ayıklayıcının çağrılacağını seçmeye yarayan bir fonksiyon tanımladım.

def select_parser(event):
    row, col = text.index("insert").split(".")
    if event.keysym == "Control_L":
        keysym.add(event.keysym)
    elif event.keysym == "v" or event.keysym == "V":
        keysym.add(event.keysym)
        if "Control_L" in keysym:
            parser_2(row, col)
    elif event.keysym == "Control_R":
        keysym.add(event.keysym)
        if "v" in keysym or "V" in keysym:
            parser_2(row, col)
    else:
        parser_1(text.get("{}.0".format(row), "{}.0".format(int(row) + 1)))
  • Bu fonksiyondaki event argümanının keysym isimli bir değişkeni var. Bu değişken tıpkı event.key gibi Tkinter’e ait özel bir isim. Bu fonksiyonda 4 adet durum tanımlanmış.

  • Bu durumlardan bir tanesi kullanıcı sol ctrl tuşuna basarsa diye başlıyor (if event.keysym == "Control_L":). Bu durum gerçekleştiğinde, keysym kümesine, sol ctrl tuşunu temsil eden anahtar simge eklenir.

  • Bir diğer durum, kullanıcı “v” veya “V” tuşuna basarsa durumu. Bu durum gerçekleştiği zaman, yine keysym kümesine basılan tuşu temsil eden anahtar simge eklenir. Ve bu koşul altında eğer daha önceden sol ctrl tuşuna basılmışsa parser_2() fonksiyonu row ve col argümanlarıyla birlikte çağrılır. parser_2() fonksiyonunun içindeki keysym = set() işlemi sayesinde keysym kümesini tazeleriz.

  • Bir diğer durum, kullanıcı sağ ctrl tuşuna basarsa durumu… Bu durum gerçekleştiği zaman, yine basılan tuşu temsil eden anahtar simge keysym kümesine eklenir. Bu koşul altında eğer daha önceden “v” veya “V” tuşlarından birine basılmışsa, yine parser_2() fonksiyonu row ve col argümanlarıyla birlikte çağrılır. parser_2() fonksiyonunun içindeki keysym = set() işlemi sayesinde keysym kümesini tazeleriz.

  • Not: sol ctrl +v tuşlarına basınca; önce “Control_L”, sonra “v” harfi okunurken, sağ ctrl +v tuşlarına basınca; önce “v” harfi sonra “Control_R” okunur. Test etmek için print(event.keysym) yazınız.

  • Son durum da, kullanıcının widgete yazı yazarken etkin olacak ayrıştırıcıyı çağıran durumdur.

parser_1(text.get("{}.0".format(row), "{}.0".format(int(row) + 1)))
  • Renklendirme işlemi row ve row + 1 satırları arasında geçerli olacak ve bu işlem için parser_1() ayrıştırıcısı kullanılıyor.

#30

Siz sorunu çözmüşsünüz ama aşağıdaki adreste biri pygments kullanarak yapmış ve herhangi bir hız problemi yok.

Düzenleme:Linki ekledim.


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

Olabilir, hangi adresten bahsediyorsunuz?


#32

Pygments’i kullanarak bir tane metin düzenleyici hazırladım. Normal bir şekilde widget içinde yazarken renklendirme işleminde sorun olmuyor. Ancak tokenize modülünün kullanıldığı örnekte olduğu gibi, widgete kopyalanan içeriği renklendiremedim henüz.

Pygments modülünün kullanıldığı uygulamanın kodları:

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

import sys

if sys.version_info.major == 2:
    exit()
elif sys.version_info.major == 3:
    # from pygments import lex
    from pygments import token
    from pygments.lexers import python
    import tkinter as tk


root = tk.Tk()
text = tk.Text(master=root, fg="white", bg="black", font="TkDefaultFont 10")
text.pack(fill="both", expand=True)

count = 0


def colorize(*args):
    global count
    row, col = text.index("insert").split(".")
    start = text.search(
        args[0],
        "{}.{}".format(row, int(col) - len(args[0])),
        "{}.{}".format(row, col))
    end = text.index("insert")
    try:
        text.tag_add(str(count), start, end)
    except tk.TclError:
        pass
    try:
        text.tag_config(str(count), foreground=args[1], font=args[2])
    except IndexError:
        text.tag_config(str(count), foreground=args[1])
    count += 1


def parser(event):
    row, col = text.index("insert").split(".")
    code = text.get("{}.0".format(row), "{}.0".format(int(row) + 1))
    tokensource = python.Python3Lexer().get_tokens(code)
    # for ttype, value in lex(code, python.Python3Lexer()):
    for ttype, value in tokensource:
        if ttype == token.Token.Literal.Number.Integer:
            colorize(value, "purple")
        elif ttype == token.Token.Keyword:
            colorize(value, "orange")
        elif ttype == token.Token.Operator.Word:
            colorize(value, "red")
        elif ttype == token.Token.Name.Builtin:
            colorize(value, "blue")
        elif ttype == token.Token.Comment.Hashbang or \
                ttype == token.Token.Comment.Single:
            colorize(value, "grey")
        elif ttype == token.Token.Keyword.Namespace:
            colorize(value, "yellow")
        elif ttype == token.Token.Namespace:
            colorize(value, "green")
        elif ttype == token.Token.Punctuation:
            colorize(value, "brown")
        elif ttype == token.Token.Literal.String.Double:
            colorize(value, "cyan")
        elif ttype == token.Name:
            colorize(value, "white")


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

#33

Yukarıda paylaştığım adreste bu sorun tekrar fonksiyonlar yazılarak çözülmüş. Yani şöyle yapın: Kopyalama işlemi için önce kopyalama işlemini yapan daha sonra parser fonksiyonunu çalıştıran bir fonksiyon yazın, binding ile ctrl+c tuşlarına basıldığında çalışmasını sağlayın. Bu yöntemi kesme ve yapıştırma işlemi için de uyguladığınızda sorununuz çözülür.


#34

Dediğiniz gibi kopyalama işlemi için bir tane fonksiyon tanımladım ama colorize() fonksiyonu bu kopyalama işlemine göre düzenlenmediği için, kopyalama işlemini colorize() fonksiyonuna uyarlamaya çalıştım. Çünkü o fonksiyon her bir karakter girişinde etkin olan bir fonksiyon.

Bu colorize fonksiyonu ile kopyalanan bütün metni renklendirebilmek için ise, karakter ekleme pozisyonunu “1.0”'dan başlamak üzere “end’e” kadar çekmek gerekiyordu. Bu işlem de tahmin edeceğiniz gibi biraz uzun süren bir işlem.

Mesela aşağıdaki kodları çalıştırırsanız, kopyalama işlemindeki karakter renklendirmesinin yavaş olduğunu göreceksiniz.

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

import sys

if sys.version_info.major == 2:
    exit()
elif sys.version_info.major == 3:
    # from pygments import lex
    from pygments import token
    from pygments.lexers import python
    import tkinter as tk
    import threading


root = tk.Tk()
text = tk.Text(master=root, fg="white", bg="black", font="TkDefaultFont 10")
text.pack(fill="both", expand=True)

count = 0


def colorize(*args):
    global count
    row, col = text.index("insert").split(".")
    start = text.search(
        args[0],
        str(row) + "." + str(int(col) - len(args[0])),
        str(row) + "." + str(int(col))
    )
    end = text.index("insert")
    try:
        text.tag_add(str(count), start, end)
    except tk.TclError:
        pass
    try:
        text.tag_config(str(count), foreground=args[1], font=args[2])
    except IndexError:
        text.tag_config(str(count), foreground=args[1])
    count += 1


def parser_1(*args):
    tokensource = python.Python3Lexer().get_tokens(args[0])
    # for ttype, value in lex(code, python.Python3Lexer()):
    for ttype, value in tokensource:
        if ttype == token.Token.Literal.Number.Integer:
            colorize(value, "purple")
        elif ttype == token.Token.Keyword:
            colorize(value, "orange")
        elif ttype == token.Token.Operator.Word:
            colorize(value, "red")
        elif ttype == token.Token.Name.Builtin:
            colorize(value, "blue")
        elif ttype == token.Token.Comment.Hashbang or \
                ttype == token.Token.Comment.Single:
            colorize(value, "grey")
        elif ttype == token.Token.Keyword.Namespace:
            colorize(value, "yellow")
        elif ttype == token.Token.Namespace:
            colorize(value, "green")
        elif ttype == token.Token.Punctuation:
            colorize(value, "brown")
        elif ttype == token.Token.Literal.String.Double:
            colorize(value, "cyan")
        elif ttype == token.Name:
            colorize(value, "white")


keysym = set()


def parser_2():
    global keysym
    keysym = set()
    row, col = text.index("insert").split(".")
    for i in range(int(row)):
        index1 = str(i + 1) + "." + "0"
        index2 = str(i + 2) + "." + "0"
        code = text.get(index1, index2)
        for j in range(len(code)):
            index = str(i + 1) + "." + str(j)
            text.mark_set("insert", index)
            parser_1(code)
            text.mark_set("insert", text.index("insert"))


def select_parser(event):
    row, col = text.index("insert").split(".")
    code = text.get("{}.0".format(row), "{}.0".format(int(row) + 1))
    if event.keysym == "Control_L":
        keysym.add(event.keysym)
    elif event.keysym == "v" or event.keysym == "V":
        keysym.add(event.keysym)
        if "Control_L" in keysym:
            t = threading.Thread(target=parser_2)
            t.daemon = True
            t.start()
    elif event.keysym == "Control_R":
        keysym.add(event.keysym)
        if "v" in keysym or "V" in keysym:
            t = threading.Thread(target=parser_2)
            t.daemon = True
            t.start()
    else:
        parser_1(code)


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

Ben de az önce bu arkadaşın yaptığı çalışmayı inceliyordum. Ve benim üzerinde çalıştığım uygulamadan daha performanslı olduğu kesin.


#35

@hasser

Bahsettiğiniz uygulamadaki kodları biraz inceledim ve aşağıdaki gibi bir özet çıkardım kendimce. Teşekkür ederim.

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

import sys

if sys.version_info.major == 2:
    exit()
elif sys.version_info.major == 3:
    from pygments.styles import get_style_by_name
    from pygments.lexers import python
    import tkinter as tk


root = tk.Tk()
text = tk.Text(master=root, fg="white", bg="black",
               font="TkDefaultFont 10")
text.pack(fill="both", expand=True)


def create_tags():
    style = get_style_by_name("default")
    for ttype, ndef in style:
        if ndef["color"]:
            foreground = "#{}".format(ndef["color"])
        else:
            foreground = None
        text.tag_configure(str(ttype), foreground=foreground)


def colorize(event):
    code = text.get("1.0", "end")
    tokensource = python.Python3Lexer().get_tokens(code)
    row1, col1, row2, col2 = 1, 0, 1, 0
    for ttype, value in tokensource:
        row2 += value.count("\n")
        if "\n" in value:
            col2 = 0
        else:
            col2 += len(value)
        index1 = "{}.{}".format(row1, col1)
        index2 = "{}.{}".format(row2, col2)
        text.tag_add(str(ttype), index1, index2)
        row1 = row2
        col1 = col2


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