如何为tkinter文本框创建“自调整”书签

2024-04-28 18:39:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用tkinter创建一个特定于函数的文本编辑器。我想添加一个书签功能,并看到了几个使用.yview()[0]或.index(INSERT)获取应添加书签的行的yview第一行号或实际行号的示例。我不知道如何根据文本框其他部分的行插入/删除调整书签。我正在使用Bryan Oakley共享的一些很棒的代码为文本框创建行号。此代码还提供了一个“代理”函数,允许创建虚拟事件来处理不同的tkinter函数(例如,插入、删除、替换等)。下面是代码(稍微调整以处理不同字体大小的间距):

class TextLineNumbers(tk.Canvas):
    def __init__(self, *args, **kwargs):
        tk.Canvas.__init__(self, *args, **kwargs)
        self.textwidget = None
        self.font_spacing = 0
        self.setfontspacing(self.font_spacing)

    def attach(self, text_widget):
        self.textwidget = text_widget

    def setfontspacing(self, spacing):
        self.font_spacing = spacing

    def redraw(self, *args):
        '''redraw line numbers'''
        self.delete("all")

        i = self.textwidget.index("@0,0")
        while True :
            dline= self.textwidget.dlineinfo(i)
            if dline is None: break
            y = dline[1] + self.font_spacing
            linenum = str(i).split(".")[0].zfill(7)
            self.create_text(2,y,anchor="nw", text=linenum)
            i = self.textwidget.index("%s+1line" % i)

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

        # create a proxy for the underlying widget
        self._orig = self._w + "_orig"
        self.tk.call("rename", self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)

    def _proxy(self, *args):
        # let the actual widget perform the requested action

        cmd = (self._orig,) + args
        result=None
        try:
            result = self.tk.call(cmd)
        except Exception:
            pass

        # generate an event if something was added or deleted,
        # or the cursor position changed

#        print(cmd, args, "=>", result)

        if (args[0] in ("insert", "replace", "delete") or
            args[0:3] == ("mark", "set", "insert") or
            args[0:2] == ("xview", "moveto") or
            args[0:2] == ("xview", "scroll") or
            args[0:2] == ("yview", "moveto") or
            args[0:2] == ("yview", "scroll")
        ):
            self.event_generate("<<Change>>", when="tail")

        if (args[0] in ("insert", "replace", "delete") ):
            self.event_generate("<<Text_Change>>", when="now")

        # return what the actual widget returned
        return result

我已经将Text_Change事件绑定到我的自定义文本小部件,它允许我跟踪更改(例如,查看是否发生了更改,并在退出时提示保存,等等)。因此,该部分工作正常

但是,我不知道如何捕获这些事件发生时插入/删除的行的详细信息。打印(cmd,args,=>;,result)提供了很多细节,我想我可以找到一种基于这些信息计算delta的方法,但这似乎是一个复杂的解决方案

我想知道是否有人以前解决过这个问题,可能会有一些建议。或者我忽略了一个更简单的解决方案

谢谢


Tags: orthetextselfinitdefargsresult
1条回答
网友
1楼 · 发布于 2024-04-28 18:39:06

文本小部件能够通过mark_set命令设置书签。标记可以像文本小部件索引一样使用。例如,可以在长文件的末尾设置一个标记“footer”,然后使用see方法使页脚可见

标记表示两个索引之间的差距。如果在索引“200.0”处设置标记,则该标记表示上一个索引与该索引之间的空间。标记将“粘”到间隙的一侧或另一侧。默认情况下,它与右边的字符保持一致,但这可以通过接受“left”或“right”的mark_gravity方法进行更改

下面是一个人为的示例,它在第200行设置书签,然后提供一个按钮跳转到该标记。请注意,即使在程序启动后,您也可以插入任意多的行,并且标记仍将“粘贴”到单词“This”(或者更准确地说,字母“T”)上

import tkinter as tk

root = tk.Tk()
text = tk.Text(root, height=20, yscrollcommand=lambda *args: vsb.set(*args))
vsb = tk.Scrollbar(root, orient="vertical", command=text.yview)
jump = tk.Button(root, text="Jump to bookmark", command=lambda: text.see("bookmark"))

jump.pack(side="top")
vsb.pack(side="right", fill="y")
text.pack(side="left", fill="both", expand=True)

for i in range(300):
    text.insert("end", f"Lorem ipsum dolar set\n")
text.insert("200.0", "This line is bookmarked\n")
text.mark_set("bookmark", "200.0")

root.mainloop()

相关问题 更多 >