1

I have a Text widget, textbox, where the user can type, and another Text widget, linenumbers, directly to the left of it which displays line numbers. I have it working, but with one problem: the more lines I create, the more the two Text widgets get out of sync when scrolled.

Here's a gif showing what happens.

And here's my code (scroll_both and update_scroll came from another StackOverflow thread):

class MainGUI:

    def scroll_both(self, action, position, type=None):
        self.textbox.yview_moveto(position)
        self.linenumbers.yview_moveto(position)

    def update_scroll(self, first, last, type=None):
        self.textbox.yview_moveto(first)
        self.linenumbers.yview_moveto(first)
        self.scrollbar_y.set(first, last)

    def on_textbox_update(self, event):
        modified = self.textbox.edit_modified()
        if modified:
            #If the number of lines in the textbox has changed:
            if self.linecount != self.textbox.index("end").split('.')[0]:
                #Update textbox's linecount
                self.linecount = self.textbox.index("end").split('.')[0]
                # Clear the line count sidebar
                self.linenumbers.config(state="normal")
                self.linenumbers.delete(0.0, "end")
                # Fill it up again
                for LineNum in range(1, int(self.linecount)):
                    self.linenumbers.insert(self.linenumbers.index("end"), str(LineNum)+"\n", "justifyright")
                    if len(str(LineNum)) == 1:
                        self.linenumbers.config(width=2)
                    else:
                        self.linenumbers.config(width=len(str(LineNum)))
                self.linenumbers.config(state="disabled")

    def __init__(self, master):
        self.master = master

        Grid.rowconfigure(master, 1, weight=1)
        Grid.columnconfigure(master, 2, weight=1)

        self.linenumbers = Text(master, width=2, borderwidth=0, takefocus=0, padx=6, fg="gray")
        self.linenumbers.config(font="TkTextFont")
        self.linenumbers.tag_configure("justifyright", justify="right")
        self.linenumbers.grid(row=0, column=1, rowspan=2, sticky=NSEW)

        self.textbox = Text(master, padx=6, borderwidth=0, wrap=NONE)
        self.textbox.config(font="TkTextFont")
        self.textbox.grid(row=0, column=2, rowspan=2, sticky=NSEW)
        self.textbox.bind("<<Modified>>", self.on_textbox_update)

        self.scrollbar_y = ttk.Scrollbar(master)
        self.scrollbar_y.grid(row=0, column=3, rowspan=2, sticky=NSEW)
        self.scrollbar_y.config(command=self.scroll_both)
        self.textbox.config(yscrollcommand=self.update_scroll)
        self.linenumbers.config(yscrollcommand=self.update_scroll)

root = Tk()
MainGUI = MainGUI(root)
root.mainloop()
Bocuma 747
  • 11
  • 2
  • For an alternate way to do line numbers which draws numbers only for the visible lines, see https://stackoverflow.com/a/16375233/7432 One advantage that implementation has is that it works with lines that are different heights, have embeded widgets, and so on. – Bryan Oakley Jul 24 '18 at 19:43
  • @BryanOakley Works great, thanks a lot. – Bocuma 747 Jul 24 '18 at 23:03
  • @BryanOakley Actually there is one issue. When I try to paste something into the textbox and there's nothing selected, it gives this error and crashes: `File "main.py", line 47, in _proxy` `result = self.tk.call(cmd)` `_tkinter.TclError: text doesn't contain any characters tagged with "sel"` – Bocuma 747 Jul 25 '18 at 02:55

0 Answers0