python-3.x 如何突出显示文本小部件的当前行?

kpbwa7wx  于 2023-06-07  发布在  Python
关注(0)|答案(3)|浏览(198)

我正在使用Python编写一个简单的GUI代码编辑器,我希望光标所在的文本行始终高亮显示。
现在,我的TextEditor类看起来像:

class TextEditor:

   def __init__(self, container):
      self.scrollbar = Scrollbar(container)
      self.scrollbar.pack(side=RIGHT, fill=Y)

      self.textbox = Text(container, height=40, undo=True, width=80,
                          font=tkFont.Font(family="Consolas", size=12))
      self.textbox.pack(side=LEFT)

      self.textbox.config(yscrollcommand=self.scrollbar.set)
      self.scrollbar.config(command=self.textbox.yview)

我该怎么做?

0vvn1miw

0vvn1miw1#

tkinter没有内置的东西直接支持这一点。然而,对于大多数用途来说,编写一个函数来轮询光标位置并定期更新突出显示就足够了。
例如:

import tkinter as tk

class MyApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.text = tk.Text(self)
        self.text.pack(side="top", fill="both", expand=True)
        self.text.tag_configure("current_line", background="#e9e9e9")
        self.text.tag_raise("sel", "current_line")

        self._highlight_current_line()

    def _highlight_current_line(self, interval=100):
        '''Updates the 'current line' highlighting every "interval" milliseconds'''
        if not self.text.winfo_exists():
            return
        self.text.tag_remove("current_line", "1.0", "end")
        self.text.tag_add("current_line", "insert linestart", "insert lineend+1c")
        self.after(interval, self._highlight_current_line, interval)

app = MyApp()
app.mainloop()

显然,间隔越长,将引入的“滞后”越多,间隔越短,使用的CPU越多,但是在两个极端之间有一个相当大的甜蜜点,几乎没有可察觉的滞后,CPU使用率有一个难以察觉的颠簸。
还有另一种方法可以做到这一点,不涉及投票,是绝对万无一失的。您可以将突出显示移动到插入光标实际移动的精确位置,但这涉及到编写一些嵌入式Tcl代码来创建实际tk小部件的代理,该小部件隐藏在Tkinter Text对象的实现中。
最后,第三种方法是为修改光标位置的所有可能事件设置自定义绑定。虽然可能,但很难做到100%正确,因为您必须考虑修改光标位置的所有事件,以及处理代码中可能在不使用事件的情况下移动光标的位置。尽管如此,使用绑定是一个非常好的解决方案,它只是需要更多的工作。

oyjwcjzk

oyjwcjzk2#

完全不需要轮询like Bryan Oakley says in his answer,也不需要在Python代码中嵌入Tcl代码。我的解决方案是只绑定到可能最终移动光标的事件,即<Key><Button-1>

import tkinter as tk

class CurrentHighlightedLineText(tk.Text):

    """Text widget with current line highlighted"""

    def __init__(self, root, *args, **kwargs):
        tk.Text.__init__(self, root, *args, **kwargs)

        self.tag_configure('currentLine', background='#e9e9e9')
        self.bind('<Key>', lambda _: self.highlightCurrentLine())
        self.bind('<Button-1>', lambda _: self.highlightCurrentLine())
        self.highlightCurrentLine(delay=0)

    def highlightCurrentLine(self, delay=10):

        def delayedHighlightCurrentLine():
            self.tag_remove('currentLine', 1.0, "end")
            self.tag_add('currentLine', 'insert linestart', 'insert lineend+1c')
        # This bound function is called before the cursor actually moves.
        # So delay checking the cursor position and moving the highlight 10 ms.

        self.after(delay, delayedHighlightCurrentLine)

if __name__ == "__main__":
    root = tk.Tk()

    text = CurrentHighlightedLineText(root)
    text.grid(row=0, column=0, sticky='nesw')

    root.grid_rowconfigure(0, weight=1)
    root.grid_columnconfigure(0, weight=1)

    root.mainloop()
shstlldc

shstlldc3#

以下是Bryan奥克利的回答:当您想要选择包含当前行的部分而不是全部的文本时,简单地突出显示当前行会变得很痛苦,因为当前行的选定部分是使用当前行的颜色而不是选定文本的颜色来着色的,所以很难一眼看出当前行的哪一部分被选中。
因此,我修改了Bryan的示例代码,如果选择了任何文本,则不突出显示当前行:

import Tkinter as tk

class MyApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.text = tk.Text(self)
        self.text.pack(side="top", fill="both", expand=True)
        self.text.tag_configure("current_line", background="#e9e9e9")
        self._highlight_current_line()

    def _highlight_current_line(self, interval=100):
        '''Rehighlights the 'current line' every "interval" milliseconds'''
        self.text.tag_remove("current_line", 1.0, "end")

        # If text is selected, DON'T highlight the current line.
        try:
            text = self.text.get(tk.SEL_FIRST, tk.SEL_LAST)
        except tk.TclError:
            self.text.tag_add("current_line",
                                "insert linestart", "insert lineend+1c")

        self.after(interval, self._highlight_current_line)

app = MyApp()
app.mainloop()

我希望这对某人有帮助。

相关问题