【问题标题】:How to display line numbers in tkinter.Text widget?如何在 tkinter.Text 小部件中显示行号?
【发布时间】:2014-07-22 19:59:29
【问题描述】:

我正在编写自己的代码编辑器,我希望它在左侧有编号的行。基于this answer我写了这个示例代码:

#!/usr/bin/env python3

import tkinter


class CodeEditor(tkinter.Frame):
    def __init__(self, root):
        tkinter.Frame.__init__(self, root)
        # Line numbers widget
        self.__line_numbers_canvas = tkinter.Canvas(self, width=40, bg='#555555', highlightbackground='#555555', highlightthickness=0)
        self.__line_numbers_canvas.pack(side=tkinter.LEFT, fill=tkinter.Y)

        self.__text = tkinter.Text(self)
        self.__text['insertbackground'] = '#ffffff'
        self.__text.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=True)

    def __update_line_numbers(self):
        self.__line_numbers_canvas.delete("all")
        i = self.__text.index('@0,0')
        self.__text.update()                    #FIX: adding line
        while True:
            dline = self.__text.dlineinfo(i)
            if dline:
                y = dline[1]
                linenum = i[0]
                self.__line_numbers_canvas.create_text(1, y, anchor="nw", text=linenum, fill='#ffffff')
                i = self.__text.index('{0}+1line'.format(i))  #FIX
            else:
                break

    def load_from_file(self, path):
        self.__text.delete('1.0', tkinter.END)
        f = open(path, 'r')
        self.__text.insert('0.0', f.read())
        f.close()
        self.__update_line_numbers()


class Application(tkinter.Tk):
    def __init__(self):
        tkinter.Tk.__init__(self)
        code_editor = CodeEditor(self)
        code_editor.pack(fill=tkinter.BOTH, expand=True)
        code_editor.load_from_file(__file__)

    def run(self):
        self.mainloop()


if __name__ == '__main__':
    app = Application()
    app.run()

不幸的是,__update_line_numbers 内部出现了问题。此方法应在我的Canvas 小部件上从上到下写入行号,但它仅打印第一行的编号 (1),然后退出。为什么?

【问题讨论】:

  • 所有行都有文字吗?
  • @furas 是的,这个例子已经可以运行了,它会加载他自己的源代码。
  • @BPS:嗯,是的,但是源代码的第 2 行是空的;如果您删除该空白行,它现在是对前 2 行编号然后停止,还是仍然只是第一行?
  • 从文本中删除空行不会改变任何事情。
  • 好的,还有两件事要测试:如果你print(self.__text.dump(previndex, i))(显然你需要在更改之前添加previndex=i,并在循环之前将其设置为某个值),你得到第2行有什么吗?只是 dlineindex('2.0') 返回 None 还是一个值?

标签: python tkinter


【解决方案1】:

根本问题是您在返回运行循环之前调用了dlineinfo,因此文本尚未布局。

正如the docs 解释:

此方法仅在文本小部件更新时有效。为了确保是这种情况,您可以先调用 update_idletasks 方法。

像往常一样,要获取更多信息,您必须求助于Tcl docs for the underlying object,它基本上告诉您文本小部件在更新之前可能不正确,哪些字符是可见的,哪些是不可见的,在这种情况下它可能返回None 不是因为任何问题,而只是因为,就它而言,您要求的是屏幕外某些东西的 bbox。

测试这是否是问题的一个好方法是在调用dlineinfo(i) 之前调用self.__text.see(i)。如果它改变了dlineinfo 的结果,这就是问题所在。 (或者,如果不是这样,至少与此相关——无论出于何种原因,Tk 认为第 1 行之后的所有内容都在屏幕外。)

但在这种情况下,即使调用update_idletasks 也不起作用,因为它不仅更新需要发生的行信息,而且首先布置文本。您需要做的是明确推迟此调用。例如,将此行添加到load_from_file 的底部,现在它可以工作了:

self.__text.after(0, self.__update_line_numbers)

您也可以在调用self.__update_line_numbers() 之前调用self.__text.update(),我认为这应该可行。


作为旁注,它真的可以帮助您在调试器下运行它,或者在循环顶部添加print(i, dline),这样您就可以看到您得到了什么,而不仅仅是猜测。

增加linenumber 并使用'{}.0'.format(linenumber) 而不是创建像@0,0+1line+1line+1line 这样(至少对我而言)不起作用的复杂索引也不会更容易。您可以调用Text.index() 将任何索引转换为规范格式,但为什么要这么难呢?你知道你想要的是1.02.03.0等等吧?

【讨论】:

  • 我可以将该行替换为:i = '{0}.0'.format(int(i[0])+1),然后下一个索引将是 2.0、3.0、4.0、. ..它仍然无法正常工作。
  • @BPS:你确定你实际上有多行,而不是,例如,一个巨大的包裹线? (如果将END 转换为 line.col 格式会得到什么?)
  • 你错了。 “1.0+1line”是一个完全可以接受的索引。这不是问题的根本原因。
  • @BryanOakley:当我传入@0,0+1line+1line+1line+1line 时,它不喜欢索引。
  • @abarnert:你一定是做错了什么。虽然丑陋,@0,0+1line+1line+1line 是完全可以接受的,并被记录在案。
【解决方案2】:

问题的根本原因是尚未在屏幕上绘制文本,因此对dlineinfo 的调用将不会返回任何有用的信息。

如果您在绘制行号之前添加对self.update() 的调用,您的代码会运行得更好一些。它不会完美运行,因为您还有其他错误。更好的是,在 GUI 空闲时调用该函数,或者在可见性事件或类似事件上调用该函数。一个好的经验法则是永远不要打电话给update,除非你明白为什么你不应该打电话给update()。然而,在这种情况下,它是相对无害的。

另一个问题是你一直在附加到 i,但在写入画布时总是使用i[0]。当您到达第 2 行时,i 将是“1.0+1line”。对于第三行,它将是“1.0+1line+1line”,依此类推。第一个字符将始终为“1”。

您应该做的是要求 tkinter 将您修改后的 i 转换为规范索引,并将其用作行号。例如:

i = self.__text.index('{0}+1line'.format(i))

这会将“1.0+1line”转换为“2.0”,将“2.0+1line”转换为“3.0”等等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-11
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多