0

Here is the code for a simple text editor, complete with a numberline on the side. The numbers themselves are a bit buggy. However, the problem is that when i make alot of lines, the numberline and the actuall text widget gets skewed. (see picture below)

https://postimg.cc/PLdX34RT

from tkinter import*
import random

class Main(Tk):
    def __init__(self):
        super().__init__()

        self.scroll = Scrollbar(self)
        self.scroll.pack(fill=Y, expand=True, side=RIGHT)

        self.numberLine = Text(self, width=5)
        self.numberLine.pack(side=LEFT)

        self.txt = Text(self)
        self.txt.pack(side=LEFT)


        self.scroll["command"] = self.onScrollbar
        self.txt["yscrollcommand"] = self.ontextScroll
        self.numberLine["yscrollcommand"] = self.ontextScroll

        self.txt.bind('<Key>', self.insert)

    def onScrollbar(self, *args):
        self.txt.yview(*args)
        self.numberLine.yview(*args)

    def ontextScroll(self, *args):
        self.scroll.set(*args)
        self.onScrollbar("moveto", args[0])

    def insert(self, b):
        self.txt.configure(state="disabled")
        self.numberLine.delete("1.0", "end")

        for i in range(int(self.txt.index('end').split(".")[0])):
            self.numberLine.insert(INSERT, f'{i}\n')

        self.txt.configure(state="normal")

app = Main()
app.mainloop()
  • This does not answer the question, but you can find an example of how to implement line numbering here: https://stackoverflow.com/questions/16369470/tkinter-adding-line-number-to-text-widget – j_4321 Jan 31 '22 at 12:34

1 Answers1

0

I think the offset appearing between the text widget and line numbers comes from the fact that both widgets do not contain the same number of lines. Indeed, there is an extra line on the line number side due to the newline inserted after the last line number. Therefore, when there are enough lines to fill the widgets, the yview (set in percent of the total height in yview_moveto()) does not correspond to the same line in both widgets.

To fix that, one can avoid inserting the last newline in insert(), e.g. by using

self.numberLine.insert("1.0", "\n".join([str(i) for i in range(int(self.txt.index('end').split(".")[0]))]))

instead of the for loop

for i in range(int(self.txt.index('end').split(".")[0])):
    self.numberLine.insert(INSERT, f'{i}\n')

I don't know what the OP intends to do and what kind of actions are expected from the user but I think it is quite inefficient to delete all the line numbers and rewrite them every time. Here is an example of how to add or delete only the line numbers that have changed:

from tkinter import*

class Main(Tk):
    def __init__(self):
        super().__init__()

        self.scroll = Scrollbar(self)
        self.scroll.pack(fill=Y, expand=True, side=RIGHT)

        self.numberLine = Text(self, width=5)
        self.numberLine.pack(side=LEFT)
        self.numberLine.insert("1.0", "1")  # first line
        self.numberLine.configure(state="disabled")

        self.txt = Text(self)
        self.txt.pack(side=LEFT)


        self.scroll["command"] = self.onScrollbar
        self.txt["yscrollcommand"] = self.ontextScroll
        self.numberLine["yscrollcommand"] = self.ontextScroll

        self.txt.bind('<KeyRelease>', self.insert)

    def onScrollbar(self, *args):
        self.txt.yview(*args)
        self.numberLine.yview(*args)

    def ontextScroll(self, *args):
        self.scroll.set(*args)
        self.onScrollbar("moveto", args[0])

    def insert(self, b):
        self.txt.configure(state="disabled")
        self.numberLine.configure(state="normal")

        row = int(str(self.txt.index('end')).split('.')[0]) - 1  # new number of lines
        row_old = int(str(self.numberLine.index('end')).split('.')[0]) - 1  # old number of lines
        print(row, row_old)
        
        if row_old < row:  # insert missing numbers
            self.numberLine.insert('end', '\n' + '\n'.join([str(i) for i in range(row_old + 1, row + 1)]))
        elif row_old > row:  # remove extra numbers
            self.numberLine.delete('%i.0' % (row + 1), 'end')
        self.numberLine.configure(state="disabled")
        self.txt.configure(state="normal")
        
app = Main()
app.mainloop()
j_4321
  • 15,431
  • 3
  • 34
  • 61