小能豆

文本 Tkinter 中正确的语法突出显示

py

我得到了一段可以使用 Tkinter Text Widget 突出显示 Python 语法的代码。但是,我仍然无法突出显示“def”后的函数名称、“class”后的类名称以及“import”后的包名称。如果缺少了某些明显的信息,请温柔地将信息传达给我。

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"

highlighter.py 有助于突出显示从 python.yaml 文件中获取的单词。

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()

阅读 17

收藏
2025-01-03

共1个答案

小能豆

为了突出显示 def 关键字后的函数名称、class 关键字后的类名称以及 import 后的包名称,您需要对代码进行一些增强。以下是改进的步骤:

改进思路

  1. 正则表达式检测:
  2. 检测 def 后的函数名,例如:def my_function.
  3. 检测 class 后的类名,例如:class MyClass.
  4. 检测 importfrom ... import 后的模块或包名,例如:import os, from sys import argv.

  5. 高亮逻辑:

  6. 使用正则表达式匹配这些模式并为它们单独配置标签。
  7. 为这些标签设置不同的颜色。

  8. 更新 python.yaml 文件:

  9. 添加类别 function_namesclass_namesimport_names

更新后的代码

更新 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']
  function_names:
    color: purple
    matches: []  # 不需要在这里指定,代码中通过正则匹配
  class_names:
    color: blue
    matches: []  # 不需要在这里指定,代码中通过正则匹配
  import_names:
    color: green
    matches: []  # 不需要在这里指定,代码中通过正则匹配
numbers:
  color: pink
strings:
  color: "#e16d5b"

更新 highlighter.py

highlight 函数中添加以下正则表达式匹配:

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")

    # Highlight function names
    self.highlight_regex(r"\bdef\s+(\w+)", "function_names")

    # Highlight class names
    self.highlight_regex(r"\bclass\s+(\w+)", "class_names")

    # Highlight import names
    self.highlight_regex(r"\bimport\s+(\w+)|\bfrom\s+(\w+)", "import_names")

解释

  1. 匹配函数名称:
    正则表达式 \bdef\s+(\w+) 用于匹配 def 后的函数名称。

  2. 匹配类名称:
    正则表达式 \bclass\s+(\w+) 用于匹配 class 后的类名称。

  3. 匹配包名称:
    正则表达式 \bimport\s+(\w+)|\bfrom\s+(\w+) 用于匹配 importfrom ... import 语句后的包名称。

  4. 新标签:
    function_namesclass_namesimport_names 配置独特的颜色,便于区分。


示例效果

在 Text 小部件中输入以下代码时:

def my_function():
    pass

class MyClass:
    pass

import os
from sys import argv

以下部分将高亮:
- my_function 为紫色。
- MyClass 为蓝色。
- ossys 为绿色。

2025-01-03