如何突出显示文本小部件的当前行?

时间:2012-03-13 20:02:07

标签: python user-interface python-3.x tkinter

我正在使用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)

我该怎么做?

3 个答案:

答案 0 :(得分:6)

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

    def _highlight_current_line(self, interval=100):
        '''Updates the 'current line' highlighting every "interval" milliseconds'''
        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)

app = MyApp()
app.mainloop()

显然,间隔时间越长,引入的“滞后”越多,间隔时间越短,使用的CPU越多,但在极端情况之间存在相当大的最佳点,几乎没有明显的滞后现象,并且难以察觉加剧CPU使用率。

还有另一种方法可以做到这一点,不涉及民意调查,绝对是万无一失的。您可以在插入光标实际移动时精确地移动突出显示,但它涉及编写一些嵌入式Tcl代码以创建隐藏在Tkinter Text对象实现中的实际tk窗口小部件的代理。

最后,第三种方法是为修改光标位置的所有可能事件设置自定义绑定。虽然可能,但是很难100%正确,因为您必须考虑修改光标位置的所有事件,以及处理可能在不使用事件的情况下移动光标的代码中的位置。尽管如此,使用绑定是一个非常好的解决方案,它只需要更多的工作。

答案 1 :(得分:2)

绝对不需要轮询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()

答案 2 :(得分:0)

仅需扩展布莱恩·奥克利(Bryan Oakley)的答案:当您要选择包含当前行的一部分但不是全部的某些文本时,仅突出显示当前行会很麻烦,因为选中了当前行的一部分使用当前行颜色(而不是所选文本颜色)进行着色,因此一眼就很难看到当前行的哪个部分被选中。

因此,我修改了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()

我希望这对某人有帮助。