1

I got a a piece of code which can highlight the syntax for Python Using Tkinter Text Widget. However, I still can't highlight the function name after 'def' and Class name after 'class' and the package name after 'import'. If something obvious is missing, please be gentle and convey the message to me.

python.yaml

categories:
  keywords:
    color:  orange
    matches: ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
  variables:
    color: red4
    matches: ['True', 'False', None]
  
  functions:
    color: "#525fe9"
    matches: ['abs',delattr','hash','memoryview','set','all','dict','help','min','setattr','any','dir','hex','next','slice','ascii','divmod','id','object','sorted','bin','enumerate','input','oct','staticmethod','bool','eval','int','open','str','breakpoint','exec','isinstance','ord','sum','bytearray','filter','issubclass','pow','super','bytes','float','iter','print','tuple','callable','format','len','property','type','chr','frozenset','list','range','vars','classmethod','getattr','locals','repr','zip','compile','globals','map','reversed','__import__','complex','hasattr','max',round']
numbers:
  color: pink
strings:
  color: "#e16d5b"

The highlighter.py helps to highlight words which it fetched from python.yaml file.

import tkinter as tk

import yaml


class Highlighter:
    def __init__(self, text_widget, syntax_file):
        self.text_widget = text_widget
        self.syntax_file = syntax_file
        self.categories = None
        self.numbers_color = "blue"

        self.disallowed_previous_chars = ["_", "-", "."]

        self.parse_syntax_file()

        self.text_widget.bind('<KeyRelease>', self.on_key_release)

    def on_key_release(self, event=None):
        self.highlight()

    def parse_syntax_file(self):
        with open(self.syntax_file, 'r') as stream:
            
            config = yaml.safe_load(stream)
            

        self.categories = config['categories']
        self.numbers_color = config['numbers']['color']
        self.strings_color = config['strings']['color']

        self.configure_tags()
    def callback_1(self,event):
        info_window = tk.Tk()
        info_window.overrideredirect(1)
        info_window.geometry("200x24+{0}+{1}".format(event.x_root-100, event.y_root-12))

        label = tk.Label(info_window, text="Word definition goes here.")
        label.pack(fill=tk.BOTH)

        info_window.bind_all("<Leave>", lambda e: info_window.destroy())  # Remove popup when pointer leaves the window
        info_window.mainloop()

    def configure_tags(self):
        for category in self.categories.keys():
            color = self.categories[category]['color']
            self.text_widget.tag_configure(category, foreground=color)

        self.text_widget.tag_configure("number", foreground=self.numbers_color)
        self.text_widget.tag_configure("string", foreground=self.strings_color)
        self.text_widget.tag_bind("string","<Enter>", self.callback_1)
    def highlight(self, event=None):
        length = tk.IntVar()
        for category in self.categories:
            matches = self.categories[category]['matches']
            for keyword in matches:
                start = 1.0
                keyword = keyword + "[^A-Za-z_-]"
                idx = self.text_widget.search(keyword, start, stopindex=tk.END, count=length, regexp=1)
                while idx:
                    char_match_found = int(str(idx).split('.')[1])
                    line_match_found = int(str(idx).split('.')[0])
                    if char_match_found > 0:
                        previous_char_index = str(line_match_found) + '.' + str(char_match_found - 1)
                        previous_char = self.text_widget.get(previous_char_index, previous_char_index + "+1c")

                        if previous_char.isalnum() or previous_char in self.disallowed_previous_chars:
                            end = f"{idx}+{length.get() - 1}c"
                            start = end
                            idx = self.text_widget.search(keyword, start, stopindex=tk.END, regexp=1)
                        else:
                            end = f"{idx}+{length.get() - 1}c"
                            self.text_widget.tag_add(category, idx, end)

                            start = end
                            idx = self.text_widget.search(keyword, start, stopindex=tk.END, regexp=1)
                    else:
                        end = f"{idx}+{length.get() - 1}c"
                        self.text_widget.tag_add(category, idx, end)

                        start = end
                        idx = self.text_widget.search(keyword, start, stopindex=tk.END, regexp=1)

        self.highlight_regex(r"(\d)+[.]?(\d)*", "number")
        self.highlight_regex(r"[\'][^\']*[\']", "string")
        self.highlight_regex(r"[\"][^\']*[\"]", "string")

    def highlight_regex(self, regex, tag):
        length = tk.IntVar()
        start = 1.0
        idx = self.text_widget.search(regex, start, stopindex=tk.END, regexp=1, count=length)
        while idx:
            end = f"{idx}+{length.get()}c"
            self.text_widget.tag_add(tag, idx, end)
            self.text_widget.tag_bind("string","<Enter>", self.callback_1)
            start = end
            idx = self.text_widget.search(regex, start, stopindex=tk.END, regexp=1, count=length)
        

if __name__ == '__main__':
    w = tk.Tk()
    x=tk.Text(w)
    x.pack(fill=tk.BOTH,expand=1)
    h = Highlighter(x, 'Path/to/python.yaml')
    w.mainloop()

  • 1
    This does not solve the problem with this code, but as an alternative you can adapt the code from https://stackoverflow.com/a/65200005/6415268 (second answer, the one using `pygments`) to do Python syntax highlighting: replace `from pygments.lexers.markup import MarkdownLexer` by `from pygments.lexers.python import PythonLexer as Lexer` and delete the `Lexer` class. – j_4321 May 10 '21 at 09:17
  • Ok, Thanks, I will check it out –  May 10 '21 at 10:33
  • I have had a look at the code, and there is nothing in it to highlight the function name after 'def' / class name after 'class': The `highlight()` function only looks for keywords. So you will have to write it yourself. Also there are some issues with the closing quotes in the python.yaml file. – j_4321 May 10 '21 at 11:37

0 Answers0