Sys.stdin'i eş zamanlı olarak renklendirmek

Rica ederim, sayenizde yeni şeyler öğreniyorum ben de. =)

1 Beğeni

Bilmukabele. :slight_smile: Şu curses modülüne çalışmaya başlayayım.

1 Beğeni

Merhabalar,

Güzel bir proje olacak gibi gözüküyor. Curses + Pygments ile yapabilirsiniz diye düşünüyorum. Syntax highlighting olayı için textbox kullanarak yaptığım bir proje vardı. Aynı mantığı Curses’e ayarlayabilirseniz, istediğinizi yapabilirsiniz bence.

https://github.com/blackvkng/Cedit/blob/master/cedit.py#L121

1 Beğeni

Merhaba, katılımınız için teşekkür ederim. Paylaştığınız linki inceleyeceğim. Daha önce curses ile ilgilendiyseniz, bana bir konuda yardım edebilir misiniz? Az önce curses konusuna çalışmaya devam ederken, yazı rengini değiştirmeye çalışıyordum ama sanırım addstr yazarak yanlış bir komut veriyorum, rica etsem aşağıdaki kodu bir inceleyebilir misiniz?

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

import curses

stdscr = curses.initscr()
curses.start_color()
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
stdscr.nodelay(1)

q = 1
x = set()
y = 0

while q != ord("q"):
    if len(x) == 3:
        stdscr.addstr(y, 0, "def", curses.color_pair(1))
        x.clear()
        y += 1
    if q == ord("d"):
        x.add("d")
    elif q == ord("e"):
        x.add("e")
    elif q == ord("f"):
        x.add("f")
    q = stdscr.getch()

stdscr.getch()
curses.endwin()

Az önce yazdığınız programı çalıştırırken bir hata aldım. Hatayı düzeltmem için ne yapmam gerekiyor?

Hata çıktısı şu:

Traceback (most recent call last):
  File "yeni.py", line 235, in <module>
    cedit = Cedit()
  File "yeni.py", line 32, in __init__
    self.confApp()
  File "yeni.py", line 81, in confApp
    defaultConf = open(os.sep.join([self.currpath, "source", "config", "default-config.json"])).read().strip()
IOError: [Errno 2] No such file or directory: '/home/tanberk/source/config/default-config.json'

Maalesef, Curses ile çalışmadım daha önce. Ama böyle bir şey değil de, bir text widget ile yaparsanız daha iyi olacağını düşünüyorum. Attığım kodu incelerseniz, ne demek istediğimi daha iyi anlayabilirsiniz.

#!/usr/bin/env python

import curses
import curses.textpad as textpad

try:
    mainwindow = curses.initscr()
    curses.cbreak(); mainwindow.keypad(1); curses.noecho()
    textpad.Textbox(mainwindow).edit()
finally:
    curses.nocbreak(); mainwindow.keypad(0); curses.echo()
    curses.endwin()```
1 Beğeni

Hımm, evet. Ben de daha ilk defa dün curses çalışmaya başladım, acemisiyim doğal olarak. Bu text widgetini inceleyeyim. Çok teşekkür ederim. Bu arada paylaştığınız linkteki kodlar bir hata verdi. O hata neyden kaynaklanıyor biliyor musunuz?

Sanırım ana dizine sadece kodları çekmişsiniz, direk repoyu klonlamalısınız, varsayılan ayarları farklı bir dosyada tutuyordum.(bkz: https://github.com/blackvkng/Cedit/blob/master/source/config/default-config.json)

1 Beğeni

Evet şimdi çalıştı, sağolun. Dediğiniz gibi stringler ayrı bir renkte, integerler ayrı bir renkte, normal yazılar, karakterler ayrı bir renkte gözüküyorlar. Güzel bir text editörü olmuş, elinize sağlık. Programı inceleyip, yeni şeyler öğrenmeye çalışırım. :slight_smile:

Sanırım bu biraz daha iyi oldu. d ler anlık olarak mavi renge burunuyor. Türkçe karakterlerle ilgili bir sorun var suan, backspace tuşu disinda silme tuşu ve diğer tuşlar da tanitilmadi. mesela enter’a basıp alt satıra geçemiyoruz, mesela program ekran kucukken acildi diyelim, ekranı buyutursek, program hata veriyor, bunların hepsinin tanitilmasi gerekiyor galiba. :slight_smile:

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

import curses

stdscr = curses.initscr()
stdscr.keypad(1)
curses.start_color() # Renklendirmeden önce renklendirici çalıştırılmalı.
# Renk çifti bilgileri oluşturulur.
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
curses.noecho()

stdscr_size = stdscr.getmaxyx() # ekranın max enlem ve boylamı
y, x = 0, 0 # imlecin başlangıç enlem ve boylamı
q = 0 # özel bir karakter olarak tanımlanacak.
      # q'ya basarak programdan çıkacağız.

while q != ord("q"):
    q = stdscr.getch() # q artık bütün karakterleri temsil ediyor.
    stdscr.addstr(y, x, chr(q), curses.color_pair(1))
    x += 1 # karakter eklemeye devam etmek için bir yandaki hücreye geçiyoruz.
    if x == stdscr_size[1] - 1: # karakterleri ekrana yerlestirirken             
        y += 1 # imleci ve ekran boyutunu hesaba katmamız gerekiyor.
        x = 0 # yoksa yeni karakterler alt satira inemez.
    if q == ord("d"):      
        x -= 1 # d bir önceki x değerinde kaldığı için oraya geri dönüyoruz.
        stdscr.addstr(y, x, chr(q), curses.color_pair(2))
        x += 1 # düzgün yazabilmek için tekrar eski haline getiriyoruz.

# q'ya bastıktan sonra normal terminal ekranına geri dönüyoruz.
curses.endwin()

Bir önceki örnekteki enlem boylam hesabını yapmaya gerek yokmuş. :slight_smile:

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

import curses

stdscr = curses.initscr()
stdscr.keypad(1)
curses.start_color()
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
curses.raw()
curses.noecho()

q = -1
k = set()

while q != ord("q"):
    q = stdscr.getch()
    stdscr.addstr(chr(q), curses.color_pair(1))
    if q == ord("d"):
        k.add("d")
    elif q == ord("e"):
        k.add("e")
    elif q == ord("f"):
        k.add("f")
        stdscr.addstr("\b\b\bdef", curses.color_pair(2))
    else:
        k = set()

curses.endwin()

Bu biraz daha iyi oldu. Ama yine olması gerektiği gibi değil.

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

import re
import curses
import keyword

stdscr = curses.initscr()
stdscr.keypad(1)
curses.start_color()
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
curses.raw()
curses.noecho()

q = -1
KWLIST = keyword.kwlist
strings = ""

while q != ord("q"):
    q = stdscr.getch()
    stdscr.addstr(chr(q), curses.color_pair(1))
    strings += chr(q)
    for kw in KWLIST:
        regex1 = re.search("[^'\"]\s{}\s$".format(kw), strings)
        regex2 = re.search("^{}\s$".format(kw), strings)
        if regex1 or regex2:
            stdscr.addstr("{}{}".format("\b" * (len(kw) + 1), "{} ".format(kw), curses.color_pair(2))
            strings = ""

curses.endwin()

Birisi bana IPython gibi bir şey yapmak istiyorsanız, curses yerine readline modülünü kullanın dedi. IPython readline modülünü kullanıyormuş. Ve işler sanırım sandığımdan daha karışık.

The readline module defines a number of functions to facilitate completion and reading/writing of history files from the Python interpreter. This module can be used directly, or via the rlcompleter module, which supports completion of Python identifiers at the interactive prompt. Settings made using this module affect the behaviour of both the interpreter’s interactive prompt and the prompts offered by the built-in input() function.

https://ipython.org/ipython-doc/3/development/how_ipython_works.html

Read–eval–print loop

1 Beğeni

Readline() da ilginç bir modülmüş. Belirlenen bazı kelimeler ilk harflerinin yazılmasından sonra tab tuşuna basınca tamamlanabiliyor.

Örnek:

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


import readline
from colorama import *

init(autoreset=False)
print(Fore.WHITE)


class KelimeTamamlayicisi:
    def __init__(self, kelimeler):
        self.kelimeler = kelimeler
        self.harf = None
        self.eslesim = None

    def complete(self, harf, sira):
        if harf != self.harf:
            self.eslesim = [i for i in self.kelimeler if i.startswith(harf)]
            self.harf = harf
        try:
            return self.eslesim[sira]
        except IndexError:
            return None


def tamamlanabilir_kelimeler(kelimeler):
    completer = KelimeTamamlayicisi(kelimeler)
    readline.parse_and_bind("tab: complete")
    readline.set_completer(completer.complete)


liste = tuple()
tamamlanabilir_kelimeler(kelimeler=liste)

while True:
    STDIN = input(">>> ")
    if STDIN == "durdur":
        break
    else:
        print(Fore.BLUE + STDIN + Fore.WHITE)
        liste += STDIN,
        tamamlanabilir_kelimeler(kelimeler=liste)
>>> import readline
>>> import rlcompleter
>>> 
>>> readline.parse_and_bind('tab: complete')
>>> 
>>> readline.
readline._READLINE_RUNTIME_VERSION             readline.get_begidx(
readline._READLINE_VERSION                     readline.get_completer(
readline.__class__(                            readline.get_completer_delims(
readline.__delattr__(                          readline.get_completion_type(
readline.__dict__                              readline.get_current_history_length(
readline.__doc__                               readline.get_endidx(
readline.__file__                              readline.get_history_item(
readline.__format__(                           readline.get_history_length(
readline.__getattribute__(                     readline.get_line_buffer(
readline.__hash__(                             readline.insert_text(
readline.__init__(                             readline.parse_and_bind(
readline.__name__                              readline.read_history_file(
readline.__new__(                              readline.read_init_file(
readline.__package__                           readline.redisplay(
readline.__reduce__(                           readline.remove_history_item(
readline.__reduce_ex__(                        readline.replace_history_item(
readline.__repr__(                             readline.set_completer(
readline.__setattr__(                          readline.set_completer_delims(
readline.__sizeof__(                           readline.set_completion_display_matches_hook(
readline.__str__(                              readline.set_history_length(
readline.__subclasshook__(                     readline.set_pre_input_hook(
readline.add_history(                          readline.set_startup_hook(
readline.clear_history(                        readline.write_history_file(
>>> readline.

Sizin işinize yarayacak olan örnek :smiley:

Bu arada, bpython için yazılmış curties modülünü inceleyebilirsiniz.

Evet, dün readline modülüne çalışmaya başladım, rlcompleter ile benzer bir işlem yapıyor. Curties’e de uygun bir zamanda bakarım, teşekkür ederim.

Merhabalar,
Bir öneriye ihtiyacım var:
Pygments’de de yeniyim.
Pygments ve readline ile birlikte çalışmaya çalışıyorum.
readline ile tab-completion’a keyword tokenlerini veya builtinleri sorunsuz bir şekilde ekleyebiliyorum.
pygments’in highlight fonksiyonunu kullanarak, print(“hello world”) ifadesini de tokenlerine göre renklendirebiliyorum.
Ancak input()'un içini readline ve pygments’i birlikte kullanarak renklendirmeye çalışırken galiba hata yapıyorum, henüz öğrenmediğim bir konu olsa gerek.

Mesela aşağıdaki kodları çalıştırdığım zaman aldığım ekran görüntüsü:

TAB’a 2 kez basınca keyword’leri görebiliyoruz. Program çalıştırılır çalıştırılmaz ekrana tokenlerine göre pygments ile ayrıştırılmış bir print(“hello world”) yazısı çıkıyor. Sonra kullanıcının yazı girişi yapacağı bir prompt var ancak pygments’i prompttaki yazılara nasıl uyarlayacağımı öğrenemedim.

import readline
from pygments.token import Token
from pygments.style import Style
from pygments.lexers import Python3Lexer
from pygments import highlight
from pygments.formatters import Terminal256Formatter
import keyword


class Completer:
    def __init__(self, words):
        self.words = words
        self.prefix = None
        self.match = None

    def complete(self, prefix, index):
        if prefix != self.prefix:
            self.match = [i for i in self.words if i.startswith(prefix)]
            self.prefix = prefix
        try:
            return self.match[index]
        except IndexError:
            return None


class MyStyle(Style):
    styles = {
        Token.String: '#ansiwhite',
        Token.Number: '#ansired',
        Token.Keyword: '#ansiyellow',
        Token.Operator: '#ansiyellow',
        Token.Name.Builtin: '#ansiblue',
        Token.Literal.String.Single: '#ansired',
        Token.Punctuation: '#ansiwhite'
    }


if __name__ == "__main__":
    code = highlight("print('hello world')", Python3Lexer(), Terminal256Formatter(style=MyStyle))
    readline.parse_and_bind('tab: complete')
    readline.set_completer(Completer(keyword.kwlist).complete)
    print(code)
    while True:
        _input = input(">>> ")
        if _input == "quit":
            break
        else:
            print(_input)

Readline’ın tamamlayacağı keyword listesinin öğelerini renklendirmeyi denedim bu sefer de aşağıdaki gibi bir çıktı alıyorum:

Bu çıktıyı verdiren kodlar:

import readline
from pygments.token import Token
from pygments.style import Style
from pygments.lexers import Python3Lexer
from pygments import highlight
from pygments.formatters import Terminal256Formatter
import keyword


class Completer:
    def __init__(self, words):
        self.words = words
        self.prefix = None
        self.match = None

    def complete(self, prefix, index):
        if prefix != self.prefix:
            self.match = [i for i in self.words if i.startswith(prefix)]
            self.prefix = prefix
        try:
            return self.match[index]
        except IndexError:
            return None


class MyStyle(Style):
    styles = {
        Token.String: '#ansiwhite',
        Token.Number: '#ansired',
        Token.Keyword: '#ansiyellow',
        Token.Operator: '#ansiyellow',
        Token.Name.Builtin: '#ansiblue',
        Token.Literal.String.Single: '#ansired',
        Token.Punctuation: '#ansiwhite'
    }


if __name__ == "__main__":
    CODE = [highlight(i, Python3Lexer(), Terminal256Formatter(style=MyStyle)) for i in keyword.kwlist]
    readline.parse_and_bind('tab: complete')
    readline.set_completer(Completer(CODE).complete)

    while True:
        _input = input(">>> ")
        if _input == "quit":
            break
        else:
            print(_input)

Muhtemelen eksik birşeyler yapıyorum mesela son örnekteki CODE’un keyword elemanları şöyle gözüküyor:

'\x1b[33;01mFalse\x1b[39;00m\n'  # Bu mesela False için.

Tamamlama işlemini ilk harften itibaren yaptığına göre, 2 kez tab’a basınca son ekran görüntüsünde gözüken garip çıktının sebebi anlaşılıyor. Önerisi olan arkadaşların önerilerini paylaşmasını isterim. Teşekkür ederim.

python-prompt-toolkit ve pygments ile sonunda oldu.

Kodlar:

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

import os
from sys import stdout, stderr
from keyword import kwlist
import builtins
from colorama import *
from pygments.lexers import Python3Lexer
from prompt_toolkit.shortcuts import prompt
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.layout.lexers import PygmentsLexer
from prompt_toolkit.contrib.completers import WordCompleter

CODE, SIZE, TAB, KWLIST, BUILTINS = "", 0, " " * 4, kwlist, dir(builtins)
KWLIST.extend(BUILTINS)
KWLIST.append("clear")
completer = WordCompleter(KWLIST)
init(autoreset=False)
history = InMemoryHistory()

while True:
    if SIZE >= 1:
        LINE = prompt(
                "... {}".format(TAB * SIZE),
                lexer=PygmentsLexer(Python3Lexer),
                completer=completer,
                history=history,
                vi_mode=True)
        CODE += "{}{}\n".format(TAB * SIZE, LINE)
        if LINE == '':
            if SIZE == 0:
                try:
                    stdout.write(Fore.WHITE)
                    exec(CODE)
                except BaseException as ERR:
                    stderr.write("{}{}\n".format(Fore.YELLOW, ERR))
                    stdout.write(Fore.WHITE)
                CODE = ""
            else:
                SIZE -= 1
        elif LINE.endswith(":"):
            SIZE += 1
        else:
            continue
    else:
        LINE = prompt(
                ">>> {}".format(TAB * SIZE),
                lexer=PygmentsLexer(Python3Lexer),
                completer=completer,
                history=history,
                vi_mode=True)
        CODE += "{}{}\n".format(TAB * SIZE, LINE)
        if not LINE.endswith(":"):
            if LINE == "exit" or LINE == "quit" or LINE == "exit()" or LINE == "quit()":
                exit()
            elif LINE == "clear":
                os.system("cls" if os.name == "nt" else "clear")
                CODE = ""
            try:
                stdout.write(Fore.WHITE)
                exec(CODE)
            except BaseException as ERR:
                stderr.write("{}{}\n".format(Fore.YELLOW, ERR))
                stdout.write(Fore.WHITE)
            CODE = ""
        else:
            SIZE += 1
3 Beğeni

Sanırım bu modül readline modülünün özelleştirilmiş ve geliştirilmiş bir hali.

Bu arada bu konudan bir “Nasıl yapılır?” kılavuzu çıkar. @dildeolupbiten, ne dersin? =)

1 Beğeni

Evet, o modülle hem de konuyla ilgili söylediklerinize katılıyorum. Ayrıca desteğiniz için size ve diğer arkadaşlara da çok teşekkür ederim. Bu arada bir önceki mesajdaki kodları güncelledim, programa python_history eklendi. Yani daha önce yazdığımız bir satır, hafızaya alınıyor, tekrar kullanmak istediğimizde, aşağı ok tuşuna basarak kullanabiliriz.

Edit: vi mode eklendi.

3 Beğeni