аватар question@mail.ru · 01.01.1970 03:00

Как сделать подсветку синтаксиса в tkinter

Я подозреваю что этот вопрос очень сложный (ну или невозможный). Но
я хотел бы узнать можно ли сделать подсветку синтаксиса в текстовом виджете ?

аватар answer@mail.ru · 01.01.1970 03:00

Предупреждение: реализация в ответе не оптимальная, будет тормозить при прокрутке на большом количестве строк. Для неучебных целей рекомендую использовать модуль


Нужно делать лексический разбор (парсинг) текста - разбивать текст на подсвечиваемые куски (токены), в зависимости от типа токена по-разному выделять куски текста. Для простоты можно взять готовый парсер, например, из библиотеки (ставится с помощью pip install pygments). Также можно использовать модуль из стандартной библиотеки python, если предполагается делать подсветку только для Python.

Ниже собранный на коленке пример с подсветкой ключевых слов и текстовых литералов (работает неоптимально, при каждом изменении удаляет всю подсветку, заново разбирает текст на токены, заново подсвечивает - в идеале нужно обрабатывать только видимые строки текста):

import tkinter as tkfrom pygments.lexers import PythonLexerfrom pygments.token import Tokenlexer = PythonLexer()root = tk.Tk()text = tk.Text(root)text.pack()# Создаем теги с разными свойствами, которые будем присваивать соответствующим типам токеновtext.tag_config(""keyword"", foreground='blue')text.tag_config(""string_literal"", foreground='red')# Прописываем соответствие типа токена тегу подсвет��иtoken_type_to_tag = {    Token.Keyword: ""keyword"",    Token.Operator.Word: ""keyword"",    Token.Name.Builtin: ""keyword"",    Token.Literal.String.Single: ""string_literal"",    Token.Literal.String.Double: ""string_literal"",}def get_text_coord(s: str, i: int):    """"""    Из индекса символа получить ""координату"" в виде ""номер_строки_текста.номер_символа_в_строке""    """"""    for row_number, line in enumerate(s.splitlines(keepends=True), 1):        if i < len(line):            retu f'{row_number}.{i}'                i -= len(line)def on_edit(event):    # Удалить все имеющиеся теги из текста    for tag in text.tag_names():        text.tag_remove(tag, 1.0, tk.END)        # Разобрать текст на токены    s = text.get(1.0, tk.END)    tokens = lexer.get_tokens_unprocessed(s)        for i, token_type, token in tokens:        print(i, token_type, repr(token))  # Отладочный вывод - тут видно какие типы токенов выдаются        j = i + len(token)        if token_type in token_type_to_tag:            text.tag_add(token_type_to_tag[token_type], get_text_coord(s, i), get_text_coord(s, j))    # Сбросить флаг редактирования текста    text.edit_modified(0)text.bind('<<Modified>>', on_edit)root.mainloop()


Как добавить обработку других токенов, на примере комментариев: вставляем в текстовое поле комментарий, например # Комментарий, смотрим, что вывелось в консоль (в коде не зря оставлен отладочный вывод):

0 Token.Comment.Single '# Комментарий'13 Token.Text '\n'

Т.е. комментарию соответствует класс токена Token.Comment.Single.

Дальше, добавляем тег с цветом для комментариев, пусть это будет серый:

text.tag_config(""comment"", foreground='gray')

В словарь соответствий классов токенов тегам tkinter добавляем класс комментария, ставим ему в соответствие только что добавленный тег:

token_type_to_tag = {    ...    Token.Comment.Single: ""comment"",}

Итог, видим, что комментарий стал выделяться серым цветом:

Последние

Похожие