I suspect that this issue is very complicated (well, or impossible). But
I would like to know if it is possible to make a syntax backlight in a text widget?
question@mail.ru
·
01.01.1970 03:00
How to make syntax backlight in tkinter
answer@mail.ru
·
01.01.1970 03:00
Waing: the implementation in the response is not optimal, it will slow down when scrolling on a large number of lines. For non-medical purposes, I recommend using the module
You need to do lexical analysis (parsing) of the text - split the text into highlighted pieces (tokens), depending on the type of token, select the pieces of text in different ways. For simplicity, you can take a ready-made parser, for example, from the library (installed using pip install pygments). You can also use a module from the python standard library if you plan to do highlighting only for Python.
Below is an example assembled on the knee with highlighting keywords and text literals (it works suboptimally, removes all the highlighting with each change, re-parses the text into tokens, re-highlights - ideally, only visible lines of text should be processed):
import tkinter as tkfrom pygments.lexers import PythonLexerfrom pygments.token import Tokenlexer = PythonLexer()root = tk.Tk ()text = tk.Text(root)text.pack()# Creating tags with different properties that we will assign to the corresponding token typestext.tag_config(""keyword"", foreground='blue')text.tag_config(""string_literal"", foreground='red')# Specifying the matching of the token type to the sublight tag andtoken_type_to_tag = { Token.ken_type_to_tag = { Token.Keyword: ""keyword"", Token.Operator.Word: ""keyworss="">""keyword"", Token.Name.Builtin: ""keyword"", Token.Literal.String.Single: ""string_literal"", Token.Literal.String.Double: ""string_literal"",}def"">def get_texget_text_coord(s: str, i: int): """""" From the index to get the ""coordinate"" in the form of thhe ""line_text number.character_in_string number"" """""" for class="">for row_number, line in enumerate(s.splitlines(keepends=True), 1): if i < len(line): returetu f'{row_n>{row_number}.{i}' i -= len(line)len(line)def on_edit(event): # Remove all existing tags from the text for tag in text.tag_names(): text.tag_remove(tag, 1.0, tk.END) #Parse text into tokens s = text.get(1.0, tk.END) tokens = lexer.okens = lexer.get_tokens_unprocessed(s) for i, token_type, token in tokens: print(i, token_type, reprrepr (token)) # Debugging output - here you can see what types of tokens are issued 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)) # Reset text editing flag text.edit_modified(0)text.bind('>)text.bind(';>' , on_edit)root.maioot.mainloop() How to add processing of other tokens, using the example of comments: we insert a comment in the text field, for example # Comment, we are looking at what is output to the console (debugging output is left in the code for a reason):
0 Token.Comment.Single '# Comment'13 Token.Text '\n'i.e. the comment corresponds to the token class Token.Comment.Single.
Next, add an tag with a color for comments, let it be gray:
text.tag_config(""comment"", foreground='gray')We add a comment class to the dictionary of matching token classes to tkinter tags, and match the tag we just added to it:
token_type_to_tag = { ... Token.Comment.Single: ""comment"",}p>As a result, we see that the comment has become grayed ou