0

我正在根据从 YouTube 教程中获得的基础制作测试编辑器。我试图突出显示python的语句,但是当我写一个语句时,它会为所有行着色,我认为问题在于我使用的索引。

这是代码:

import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox


class Menubar:

    def __init__(self, parent):
        font_specs = 14
        
        menubar = tk.Menu(parent.master)
        parent.master.config(menu = menubar)

        file_dropdown = tk.Menu(menubar, font = font_specs, tearoff = 0)
        file_dropdown.add_command(label = "Nuovo file",
                                  accelerator = "Ctrl + N",
                                  command = parent.new_file)

        file_dropdown.add_command(label = "Apri file",
                                  accelerator = "Ctrl + O",
                                  command = parent.open_file)

        file_dropdown.add_command(label = "Salva",
                                  accelerator = "Ctrl + S",
                                  command = parent.save)

        file_dropdown.add_command(label = "Salva con nome",
                                  accelerator = "Ctrl + Shit + S",
                                  command = parent.save_as)

        file_dropdown.add_separator()

        file_dropdown.add_command(label = "Esci",
                                  command = parent.master.destroy)

        about_dropdown = tk.Menu(menubar, font = font_specs, tearoff = 0)
        about_dropdown.add_command(label = "Note di rilascio",
                                   command = self.show_about_message)

        about_dropdown.add_separator()

        about_dropdown.add_command(label = "About",
                                   command = self.show_release_notes)

        settings_dropdown = tk.Menu(menubar, font = font_specs, tearoff = 0)
        settings_dropdown.add_command(label = "Cambia lo sfondo dell'editor",
                                      command = parent.change_background)

        menubar.add_cascade(label = "File", menu = file_dropdown)
        menubar.add_cascade(label = "About", menu = about_dropdown)
        menubar.add_cascade(label = "Settings", menu = settings_dropdown)


    def show_about_message(self):
        box_title = "Riguardo PyText"
        box_message = "Il mio primo editor testuale creato con Python e TkInter!"

        messagebox.showinfo(box_title, box_message)


    def show_release_notes(self):
        box_title = "Note di Rilascio"
        box_message = "Versione 0.1 (Beta) Santa"

        messagebox.showinfo(box_title, box_message)
        

class Statusbar:

    def __init__(self, parent):
        font_specs = 12

        self.status = tk.StringVar()
        self.status.set("PyText - 0.1 Santa")

        label = tk.Label(parent.text_area, textvariable = self.status,
                         fg = "black", bg = "lightgrey", anchor = "sw")

        label.pack(side = tk.BOTTOM, fill = tk.BOTH)

    def update_status(self, *args):
        if isinstance(args[0], bool):
            self.status.set("Il tuo file è stato salvato!")

        else:
            self.status.set("PyText - 0.1 Santa")


class PyText:
    """
    Classe-Madre dell'applicazione
    """

    def __init__(self, master):
        master.title("Untitled - PyText")
        master.geometry("1200x700")

        font_specs = 18
        
        self.master = master
        self.filename = None
        
        self.text_area = tk.Text(master, font = font_specs, insertbackground = "black")
        self.scroll = tk.Scrollbar(master, command = self.text_area.yview)
        self.text_area.configure(yscrollcommand = self.scroll.set)
        self.text_area.pack(side = tk.LEFT, fill = tk.BOTH, expand = True)
        self.scroll.pack(side = tk.RIGHT, fill = tk.Y)

        self.menubar = Menubar(self)
        self.statusbar = Statusbar(self)
        self.bind_shortcuts()

        
    def set_window_title(self, name = None):
        if name:
            self.master.title(name + " - PyText")
            
        else:
            self.master.title("Untitled - PyText")    


    def new_file(self, *args):
        self.text_area.delete(1.0, tk.END)
        self.filename = None

        self.set_window_title()


    def open_file(self, *args):
        self.filename = filedialog.askopenfilename(
            defaultextension = ".txt",
            filetypes = [("Tutti i file", "*.*"),
                         ("File di Testo", "*.txt"),
                         ("Script Python", "*.py"),
                         ("Markdown Text", "*.md"),
                         ("File JavaScript", "*.js"),
                         ("Documenti Html", "*.html"),
                         ("Documenti CSS", "*.css"),
                         ("Programmi Java", "*.java")]
        )

        if self.filename:
            self.text_area.delete(1.0, tk.END)

            with open(self.filename, "r") as f:
                self.text_area.insert(1.0, f.read())

            self.set_window_title(self.filename)


    def save(self, *args):
        if self.filename:
            try:
                textarea_content = self.text_area.get(1.0, tk.END)
                with open(self.filename, "w") as f:
                    f.write(textarea_content)

                self.statusbar.update_status(True)

            except Exception as e:
                print(e)

        else:
            self.save_as()


    def save_as(self, *args):
        try:
            new_file = filedialog.asksaveasfilename(
                initialfile = "Untitled.txt",
                defaultextension = ".txt",
                filetypes = [("Tutti i file", "*.*"),
                             ("File di Testo", "*.txt"),
                             ("Script Python", "*.py"),
                             ("Markdown Text", "*.md"),
                             ("File JavaScript", "*.js"),
                             ("Documenti Html", "*.html"),
                             ("Documenti CSS", "*.css"),
                             ("Programmi Java", "*.java")]
                )

            textarea_content = self.text_area.get(1.0, tk.END)
            with open(new_file, "w") as f:
                f.write(textarea_content)

            self.filename = new_file
            self.set_window_title(self.filename)
            self.statusbar.update_status(True)

        except Exception as e:
            print(e)


    def change_background(self):
        self.text_area.config(background = "black", foreground = "white", 
                              insertbackground = "white", insertwidth = 2)

        
    def highlight_text(self, *args):
        tags = {
            "import": "pink",
            "from": "pink",
            "def": "blue",
            "for": "purple",
            "while": "purple"
        }

        textarea_content = self.text_area.get(1.0, tk.END)
        lines = textarea_content.split("\n")

        for row in lines:
            for tag in tags:
                index = row.find(tag) + 1.0

                if index > 0.0:
                    self.text_area.tag_add(tag, index, index + len(tag) -1)
                    self.text_area.tag_config(tag, foreground = tags.get(tag))

                    print("Nuovo tag aggiunto:", tag)
            
        print("Funzione eseguita:", args, "\n")
    
    
    def bind_shortcuts(self):
        self.text_area.bind("<Control-n>", self.new_file)
        self.text_area.bind("<Control-o>", self.open_file)
        self.text_area.bind("<Control-s>", self.save)
        self.text_area.bind("<Control-S>", self.save_as)
        self.text_area.bind("<Key>", self.highlight_text, self.statusbar.update_status)

    
if __name__ == "__main__":
    master = tk.Tk()
    pt = PyText(master)
    master.mainloop()

如何获取语句所在行的索引?

4

1 回答 1

1

您可以单独处理每一行,因此您只能得到X. 为了让Y你需要enumerate线路:

for y, row in enumerate(lines, 1):

find()您转换为的结果,float但您需要int并稍后转换X,Y为字符串"Y.X"

start = '{}.{}'.format(y, x)
end   = '{}.{}'.format(y, x+len(tag))

适合我的版本

for y, row in enumerate(lines, 1):
    for tag in tags:

        x = row.find(tag)

        if x > -1:
            print(f"{tag} | x: {x} | y: {y}")

            start = '{}.{}'.format(y, x)
            end   = '{}.{}'.format(y, x+len(tag))

            print(f"{tag} | start: {start} | end: {end}")

            self.text_area.tag_add(tag, start, end)
            self.text_area.tag_config(tag, foreground = tags.get(tag))

你的想法只有一个大问题 - 它会def在 worddefineforwordforward等中着色。所以也许它需要regex创建更复杂的规则并只搜索完整的单词。

其他问题:find()只给你第一个元素 - 所以如果你试图突出显示可以排两次的元素,那么你将不得不使用循环find(..., start)来搜索第一个元素。

也许最好使用示例来突出显示 tkinter Text 小部件中的文本


顺便提一句:

您将快捷键绑定到,self.text_area所以我必须单击文本区域才能使用快捷键。如果我更改self.text_area.bindself.master.bind,即使不单击文本区域,快捷方式也可以使用。


编辑:

有使用Thonny IDETkinter并突出显示代码。

我试图找到它是如何做到这一点的,但我只找到了高亮的正则表达式 - thonny_utils.py

也许如果你得到完整的代码并使用一些工具在文件中搜索字符串(比如grep在 Linux 中),那么你可以找到它使用变量的所有地方token_utils.py(即。KEYWORD

编辑: 着色.py


编辑:

highlight_text具有使用先前方法line.findhighlight_text_regex使用text_area.searchwith的函数的完整代码regex

基于问题答案的代码的新版本如何在 tkinter 文本小部件中突出显示文本

在此处输入图像描述

import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import os

print(os.getcwd())

class Menubar:

    def __init__(self, parent):
        font_specs = 14
        
        menubar = tk.Menu(parent.master)
        parent.master.config(menu = menubar)

        file_dropdown = tk.Menu(menubar, font = font_specs, tearoff = 0)
        file_dropdown.add_command(label = "Nuovo file",
                                  accelerator = "Ctrl + N",
                                  command = parent.new_file)

        file_dropdown.add_command(label = "Apri file",
                                  accelerator = "Ctrl + O",
                                  command = parent.open_file)

        file_dropdown.add_command(label = "Salva",
                                  accelerator = "Ctrl + S",
                                  command = parent.save)

        file_dropdown.add_command(label = "Salva con nome",
                                  accelerator = "Ctrl + Shift + S",
                                  command = parent.save_as)

        file_dropdown.add_separator()

        file_dropdown.add_command(label = "Esci",
                                  accelerator = "Ctrl + Q",
                                  command = parent.master.destroy)

        about_dropdown = tk.Menu(menubar, font = font_specs, tearoff = 0)
        about_dropdown.add_command(label = "Note di rilascio",
                                   command = self.show_about_message)

        about_dropdown.add_separator()

        about_dropdown.add_command(label = "About",
                                   command = self.show_release_notes)

        settings_dropdown = tk.Menu(menubar, font = font_specs, tearoff = 0)
        settings_dropdown.add_command(label = "Cambia lo sfondo dell'editor",
                                      command = parent.change_background)

        menubar.add_cascade(label = "File", menu = file_dropdown)
        menubar.add_cascade(label = "About", menu = about_dropdown)
        menubar.add_cascade(label = "Settings", menu = settings_dropdown)


    def show_about_message(self):
        box_title = "Riguardo PyText"
        box_message = "Il mio primo editor testuale creato con Python e TkInter!"

        messagebox.showinfo(box_title, box_message)


    def show_release_notes(self):
        box_title = "Note di Rilascio"
        box_message = "Versione 0.1 (Beta) Santa"

        messagebox.showinfo(box_title, box_message)
        

class Statusbar:

    def __init__(self, parent):
        font_specs = 12

        self.status = tk.StringVar()
        self.status.set("PyText - 0.1 Santa")

        label = tk.Label(parent.text_area, textvariable = self.status,
                         fg = "black", bg = "lightgrey", anchor = "sw")

        label.pack(side = tk.BOTTOM, fill = tk.BOTH)

    def update_status(self, *args):
        if isinstance(args[0], bool):
            self.status.set("Il tuo file è stato salvato!")

        else:
            self.status.set("PyText - 0.1 Santa")


class PyText:
    """
    Classe-Madre dell'applicazione
    """

    def __init__(self, master):
        master.title("Untitled - PyText")
        master.geometry("1200x700")

        font_specs = 18
        
        self.master = master
        self.filename = None
        
        self.text_area = tk.Text(master, font = font_specs, insertbackground = "black")
        self.scroll = tk.Scrollbar(master, command = self.text_area.yview)
        self.text_area.configure(yscrollcommand = self.scroll.set)
        self.text_area.pack(side = tk.LEFT, fill = tk.BOTH, expand = True)
        self.scroll.pack(side = tk.RIGHT, fill = tk.Y)

        self.menubar = Menubar(self)
        self.statusbar = Statusbar(self)
        self.bind_shortcuts()

        
    def set_window_title(self, name = None):
        if name:
            self.master.title(name + " - PyText")
            
        else:
            self.master.title("Untitled - PyText")    


    def new_file(self, *args):
        self.text_area.delete(1.0, tk.END)
        self.filename = None

        self.set_window_title()


    def open_file(self, *args):
        self.filename = filedialog.askopenfilename(
            initialdir = os.getcwd(),
            defaultextension = ".txt",
            filetypes = [("Tutti i file", "*.*"),
                         ("File di Testo", "*.txt"),
                         ("Script Python", "*.py"),
                         ("Markdown Text", "*.md"),
                         ("File JavaScript", "*.js"),
                         ("Documenti Html", "*.html"),
                         ("Documenti CSS", "*.css"),
                         ("Programmi Java", "*.java")]
        )

        if self.filename:
            self.text_area.delete(1.0, tk.END)

            with open(self.filename, "r") as f:
                self.text_area.insert(1.0, f.read())

            self.set_window_title(self.filename)


    def save(self, *args):
        if self.filename:
            try:
                textarea_content = self.text_area.get(1.0, tk.END)
                with open(self.filename, "w") as f:
                    f.write(textarea_content)

                self.statusbar.update_status(True)

            except Exception as e:
                print(e)

        else:
            self.save_as()


    def save_as(self, *args):
        try:
            new_file = filedialog.asksaveasfilename(
                initialfile = "Untitled.txt",
                defaultextension = ".txt",
                filetypes = [("Tutti i file", "*.*"),
                             ("File di Testo", "*.txt"),
                             ("Script Python", "*.py"),
                             ("Markdown Text", "*.md"),
                             ("File JavaScript", "*.js"),
                             ("Documenti Html", "*.html"),
                             ("Documenti CSS", "*.css"),
                             ("Programmi Java", "*.java")]
                )

            textarea_content = self.text_area.get(1.0, tk.END)
            with open(new_file, "w") as f:
                f.write(textarea_content)

            self.filename = new_file
            self.set_window_title(self.filename)
            self.statusbar.update_status(True)

        except Exception as e:
            print(e)


    def change_background(self):
        self.text_area.config(background = "black", foreground = "white", 
                              insertbackground = "white", insertwidth = 2)

        
    def highlight_text_old(self, *args):
        tags = {
            "import": "pink",
            "from": "red",
            "def": "blue",
            "for": "purple",
            "while": "green",
        }

        textarea_content = self.text_area.get(1.0, tk.END)
        lines = textarea_content.split("\n")

        for y, row in enumerate(lines, 1):
            for tag in tags:
                x = row.find(tag)
                if x > -1:
                    print(f"{tag} | x: {x} | y: {y}")
                    start = '{}.{}'.format(y, x)
                    end   = '{}.{}'.format(y, x+len(tag))
                    print(f"{tag} | start: {start} | end: {end}")
                    self.text_area.tag_add(tag, start, end)
                    self.text_area.tag_config(tag, foreground = tags.get(tag))

                    #print("Nuovo tag aggiunto:", tag)
            
        #print("Funzione eseguita:", args, "\n")

    def highlight_text(self, *args):
        # TODO: move to `__init__` ?
        tags = {
            "import": "pink",
            "from": "red",
            "def": "blue",
            "for": "purple",
            "while": "green",
        }

        # TODO: move to `__init__` ?
        # create tags with assigned color - do it only onve (in __init__)
        for color in ['pink', 'red', 'blue', 'purple', 'green']:
            self.text_area.tag_config(color, foreground=color)

        # remove all tags from text
        for tag in self.text_area.tag_names():
            self.text_area.tag_remove(tag, '1.0', 'end')  # not `tag_remove()`
         

        textarea_content = self.text_area.get(1.0, tk.END)
        lines = textarea_content.split("\n")

        for y, row in enumerate(lines, 1):
            for tag in tags:
                x = row.find(tag)
                if x > -1:
                    print(f"{tag} | x: {x} | y: {y}")
                    match_start = '{}.{}'.format(y, x)
                    match_end   = '{}.{}'.format(y, x+len(tag))
                    print(f"{tag} | start: {match_start} | end: {match_end}")
                    self.text_area.tag_add(tag, match_start, match_end)
                    #self.text_area.tag_config(tag, foreground=tags.get(tag))  # create tags only once - at start

                    #print("Nuovo tag aggiunto:", tag)
            
        #print("Funzione eseguita:", args, "\n")


    def highlight_text_regex(self, *args):
        # TODO: move to `__init__` ?
        tags = {
            "import": "red",
            "from": "red",
            "as": "red",

            "def": "blue",
            "class": "blue",

            "for": "green",
            "while": "green",

            "if": "brown",
            "elif": "brown",
            "else": "brown",

            "print": "purple",            

            "True": "blue",
            "False": "blue",
            "self": "blue",

            "\d+": "red",  # digits

            "__[a-zA-Z][a-zA-Z0-9_]*__": "red",  # ie. `__init__`
        }

        # add `\m \M` to words
        tags = {f'\m{word}\M': tag for word, tag in tags.items()}

        # tags which doesn't work with  `\m \M`
        other_tags = {
            "\(": "brown",  # need `\` because `(` has special meaning
            "\)": "brown",  # need `\` because `)` has special meaning

            ">=": "green",
            "<=": "green",
            "=": "green",
            ">": "green",
            "<": "green",

            "#.*$": "brown",  # comment - to the end of line `$`
        }

        # create one dictionary with all tags
        tags.update(other_tags)

        # TODO: move to `__init__` ?
        # create tags with assigned color - do it only onve (in __init__)
        for color in ['pink', 'red', 'blue', 'purple', 'green', 'brown', 'yellow']:
            self.text_area.tag_config(color, foreground=color)

        # remove all tags from text before adding all tags again (to change color when ie. `def` change to `define`)
        for tag in self.text_area.tag_names():
            self.text_area.tag_remove(tag, '1.0', 'end')  # not `tag_remove()`

        count_chars = tk.IntVar() # needs to count matched chars - ie. for digits `\d+`
        # search `word` and add `tag`
        for word, tag in tags.items():
            #pattern = f'\m{word}\M'  # http://tcl.tk/man/tcl8.5/TclCmd/re_syntax.htm#M72
            pattern = word  # http://tcl.tk/man/tcl8.5/TclCmd/re_syntax.htm#M72
            search_start = '1.0'
            search_end   = 'end'
            while True:
                position = self.text_area.search(pattern, search_start, search_end, count=count_chars, regexp=True)
                print('search:', word, position)
                if position:
                    print(f"{word} | pos: {position}")
                    match_start = position
                    match_end   = '{}+{}c'.format(position, count_chars.get()) #len(word)) # use special string `Y.X+Nc` instead values (Y, X+N)
                    print(f"{word} | start: {match_start} | end: {match_end}")
                    self.text_area.tag_add(tag, match_start, match_end)
                    #self.text_area.tag_config(tag, foreground=tags.get(tag))  # create tags only once - at start
                    search_start = match_end  # to search next word
                else:
                    break    

    def quit(self, *args):
        self.master.destroy()

    def bind_shortcuts(self):
        self.master.bind("<Control-n>", self.new_file)
        self.master.bind("<Control-o>", self.open_file)
        self.master.bind("<Control-s>", self.save)
        self.master.bind("<Control-S>", self.save_as)
        self.master.bind("<Control-q>", self.quit)
        self.master.bind("<Key>", self.highlight_text_regex, self.statusbar.update_status)

    
if __name__ == "__main__":
    master = tk.Tk()
    pt = PyText(master)
    master.mainloop()
于 2020-12-11T11:54:39.617 回答