Как создать «самонастраивающиеся» закладки для текстового поля tkinter - PullRequest
0 голосов
/ 09 апреля 2020

Я создаю текстовый редактор с указанием функции c, используя tkinter. Я хотел бы добавить возможность закладки и видел несколько примеров использования .yview () [0] или .index (INSERT) для получения номера первой строки yview или фактического номера строки, в которую должна быть добавлена ​​закладка. , Что я не знаю, так это как настроить закладки на основе вставок / удалений строк в других частях текстового поля. Я использую отличный код, которым поделился Брайан Окли, для создания номеров строк для текстового поля. Этот код также предоставляет функцию «прокси», которая позволяет создавать виртуальные события для обработки различных функций tkinter (например, вставка, удаление, замена и т. Д. c). Вот код (слегка подправлен для обработки пробелов для другого шрифта) размеры):

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 с моим пользовательским текстовым виджетом, и это позволяет мне отслеживать изменения (т. е. увидеть, произошли ли изменения и запросить сохранение при выходе, et c.). Так что эта часть работает нормально.

Однако я не знаю, как получить информацию о строках, которые были вставлены / удалены при возникновении этих событий. Печать (cmd, args, "=>", result) дает много деталей, и я думаю, я мог бы найти способ вычислить дельты на основе этой информации, но это кажется сложным решением.

Мне было интересно, если бы кто-нибудь решал эту проблему раньше и может иметь предложения. Или, может быть, есть гораздо более простое решение, которое я пропускаю.

Спасибо

1 Ответ

1 голос
/ 09 апреля 2020

Текстовый виджет имеет возможность устанавливать закладки с помощью команды mark_set. Метки могут использоваться как индекс текстового виджета. Например, вы можете установить метку «нижний колонтитул» в конце длинного файла, а затем использовать метод see, чтобы сделать нижний колонтитул видимым.

Метки представляют собой разрыв между двумя индексами. Если вы установите метку в индексе «200.0», эта метка представляет собой пространство между предыдущим индексом и этим индексом. Метка будет «прилипать» к одной или другой стороне промежутка. По умолчанию он придерживается символа справа, но это можно изменить с помощью метода 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()
...