Python Forum
Line numbers in Text widget
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Line numbers in Text widget
#1
I've been looking at adding line numbers to a Text widget. There are several examples floating around the internet, all a bit different in most cases.

I found one that seemed fairly simple to implement as I have only been working with Python for a couple of months.

The code shown below almost works 100% but needs two modifications and this is where I need some help/guidance:

(1) when the app starts up, the line numbers are not filled out...until you press a key.

(2) when a key has been pressed and the line numbers are filled out, scrolling the text down does not change the line numbers. They are always what you see after the first key press. Obviously the line numbers should scroll along with the text but I am not sure how to do that.

Thank you for any help...

import tkinter as tk

class LineNumbers(tk.Text):
    def __init__(self, master, text_widget, **kwargs):
        super().__init__(master, **kwargs)

        self.text_widget = text_widget
        self.text_widget.bind('<KeyPress>', self.on_key_press)

        self.insert(1.0, '1')
        self.configure(state='disabled')

    def on_key_press(self, event=None):
        final_index = str(self.text_widget.index(tk.END))
        num_of_lines = final_index.split('.')[0]
        line_numbers_string = "\n".join(str(no + 1) for no in range(int(num_of_lines)))
        width = len(str(num_of_lines))

        self.configure(state='normal', width=width)
        self.delete(1.0, tk.END)
        self.insert(1.0, line_numbers_string)
        self.configure(state='disabled')

if __name__ == '__main__':
    w = tk.Tk()
    t = tk.Text(w)
    l = LineNumbers(w, t, width=2)
    t.insert('1.0', 'a\n' \
                    'b\n' \
                    'c\n' \
                    'd\n' \
                    'e\n' \
                    'f\n' \
                    'g\n' \
                    'h\n' \
                    'i\n' \
                    'j\n' \
                    'k\n' \
                    'l\n' \
                    'm\n' \
                    'n\n' \
                    'o\n' \
                    'p\n' \
                    'q\n' \
                    'r\n' \
                    's\n' \
                    't\n' \
                    'u\n' \
                    'v\n' \
                    'w\n' \
                    'x\n' \
                    'y\n' \
                    'z\n' \
                    '1\n' \
                    '2\n' \
                    '3\n' \
                    '4\n' \
                    '5\n' \
                    '6\n' \
                    '7\n' \
                    '8\n' \
                    '9\n')
    l.pack(side=tk.LEFT)
    t.pack(side=tk.LEFT, expand=1)
    w.mainloop()
Reply
#2
This version seems to work
import tkinter as tk
 
class LineNumbers(tk.Text):
    def __init__(self, master, text_widget, **kwargs):
        super().__init__(master, **kwargs)
 
        self.text_widget = text_widget
        self.text_widget.bind('<KeyRelease>', self.on_key_release)
 
        self.insert(1.0, '1')
        self.configure(state='disabled')
 
    def on_key_release(self, event=None):
        p, q = self.text_widget.index("@0,0").split('.')
        p = int(p)
        final_index = str(self.text_widget.index(tk.END))
        num_of_lines = final_index.split('.')[0]
        line_numbers_string = "\n".join(str(p + no) for no in range(int(num_of_lines)))
        width = len(str(num_of_lines))
 
        self.configure(state='normal', width=width)
        self.delete(1.0, tk.END)
        self.insert(1.0, line_numbers_string)
        self.configure(state='disabled')
 
if __name__ == '__main__':
    w = tk.Tk()
    t = tk.Text(w)
    l = LineNumbers(w, t, width=2)
    t.insert('1.0', 'a\n' \
                    'b\n' \
                    'c\n' \
                    'd\n' \
                    'e\n' \
                    'f\n' \
                    'g\n' \
                    'h\n' \
                    'i\n' \
                    'j\n' \
                    'k\n' \
                    'l\n' \
                    'm\n' \
                    'n\n' \
                    'o\n' \
                    'p\n' \
                    'q\n' \
                    'r\n' \
                    's\n' \
                    't\n' \
                    'u\n' \
                    'v\n' \
                    'w\n' \
                    'x\n' \
                    'y\n' \
                    'z\n' \
                    '1\n' \
                    '2\n' \
                    '3\n' \
                    '4\n' \
                    '5\n' \
                    '6\n' \
                    '7\n' \
                    '8\n' \
                    '9\n')
    l.pack(side=tk.LEFT)
    t.pack(side=tk.LEFT, expand=1)
    w.mainloop()
Reply
#3
Thanks for that. Can you explain what your lines 14 and 15 are doing?
Reply
#4
I added some code to improve it and noted some issues:

(1) Here is a link to a test file with 200 lines, with each line numbered.

line_test.txt file

(2) when the app starts up, the text widget gets the focus right away so the line numbers are there from the start.

(3) I added a binding so you can mouse wheel (scroll) down in the text file. However, doing so causes the line numbers to get out of sync. I suspect it's because one mouse wheel click inputs more than one line down movement like the keyboard down arrow does: one line down to one key press. The wheel is multiple lines down to one wheel click. I'm not sure how to fix this, but if you can point me in the right area to look at, I might be able to figure it out. Mouse wheel clicks to how many lines move up or down is configurable, so I don't know if that plays into a fix or not. But I'll have a look and see what I can come up with if you can point me in the right direction.

(4) The PgDn/PgUp keys have a similar effect, but not for all PgDn clicks for some reason. I can hit PgDn a few times and notice the Text widget lines are out of sync with the line numbers as well as not horizontally aligned. If you can tell me what controls the horizontal alignment, I probably can make an adjustment and fix that.

Thank you again...

from tkinter import *

class LineNumbers(Text):
    def __init__(self, master, text_widget, **kwargs):
        super().__init__(master, **kwargs)

        self.text_widget = text_widget
        self.text_widget.bind('<KeyRelease>', self.on_key_release)
        self.text_widget.bind('<FocusIn>', self.on_key_release)
        self.text_widget.bind('<MouseWheel>', self.on_key_release)

        self.insert(1.0, '1')
        self.configure(state='disabled')

    def on_key_release(self, event=None):
        p, q = self.text_widget.index("@0,0").split('.')
        p = int(p)
        final_index = str(self.text_widget.index(END))
        num_of_lines = final_index.split('.')[0]
        line_numbers_string = "\n".join(str(p + no) for no in range(int(num_of_lines)))
        width = len(str(num_of_lines))

        self.configure(state='normal', width=width)
        self.delete(1.0, END)
        self.insert(1.0, line_numbers_string)
        self.configure(state='disabled')


if __name__ == '__main__':
    win = Tk()
    win.title("Line Numbers Test")
    win.geometry("800x600+1000+300")

    txt = Text(win)
    ln = LineNumbers(win, txt, width=2)

    f = open("line_test.txt", 'r')
    lines = f.readlines()
    for line in lines:
        txt.insert(END, line)
    f.close()

    ln.pack(side=LEFT, fill=BOTH)
    txt.pack(expand=True, fill=BOTH)
    txt.focus()
    win.mainloop()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] [Solved]Change text color of one line in TextBrowser Extra 2 4,757 Aug-23-2022, 09:11 PM
Last Post: Extra
  [Tkinter] The Text in the Label widget Tkinter cuts off the Long text in the view malmustafa 4 4,665 Jun-26-2022, 06:26 PM
Last Post: menator01
  [Tkinter] Text widget inert mode on and off rfresh737 5 3,782 Apr-19-2021, 02:18 PM
Last Post: joe_momma
  tkinter text widget word wrap position chrisdb 6 7,444 Mar-18-2021, 03:55 PM
Last Post: chrisdb
  [PyGTK] How to center text on multi-line buttons? Lomax 3 4,151 Jan-23-2021, 03:23 PM
Last Post: Lomax
  [Tkinter] Get the last entry in my text widget Pedroski55 3 6,294 Jul-13-2020, 10:34 PM
Last Post: Pedroski55
  How to place global tk text widget in class or on canvas puje 1 2,281 Jul-04-2020, 09:25 AM
Last Post: deanhystad
  [Tkinter] manipulation of string in Text widget Stauricus 2 2,974 Feb-17-2020, 09:23 PM
Last Post: Stauricus
  [Tkinter] Paste Operation not working in Text Widget Code_Enthusiast 1 2,907 Sep-11-2019, 08:49 PM
Last Post: Larz60+
  Part of code is adding extra new line when saving to text file. lovepeace 9 4,863 Aug-24-2019, 12:52 PM
Last Post: lovepeace

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020