The LineNumbers class

Our LineNumbers class will remain as a disabled Text widget but will no longer just hold 100 numbers all of the time. Instead, it will respond to the size of the TextArea (or any Text widget) it is linked to and update its own content accordingly:

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')

Since the LineNumbers widget is just a Text widget, it will need a reference to a master object as well as the Text widget it will be paired to. We then capture any keyword arguments and pass them to the superclass's __init__ method.

The Text widget it is paired to is assigned to an attribute and a method is bound to its <KeyPress> event. Note that we cannot bind to its <KeyRelease> event since that is already being handled by our Highlighter class.

After binding, the widget's content is set to just the number 1 and it is disabled as before.

The only method needed on this class is the one bound to our Text widget's class <KeyPress>:

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 = " ".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')

Whenever a character is added to our Text widget, we will need to find the final index inside it again. This will tell us the total amount of lines, since the first character of the index is the line number.

We gather this information by calling the index method of the END constant and casting the resulting index to a string. This string is then split on the full stop character and we take off the first number—our final line number.

This line number is passed to the range function, allowing us to get every previous number, which we join into a string on a newline character just like before.

We can use this line number to calculate the necessary width of our LineNumbers widget. Calling the len function on it gives us this number.

Now that we have all of the necessary information, we can begin configuring.

The state is changed back to normal to allow text updates, and the width is set to our calculated width. All current content is cleared and the new line numbers string is added in. Finally, the widget is disabled again so that the user cannot type into it.

Once again, we can make this module usable on its own if we want to try it out:

if __name__ == '__main__':
w = tk.Tk()
t = tk.Text(w)
l = LineNumbers(w, t, width=1)
l.pack(side=tk.LEFT)
t.pack(side=tk.LEFT, expand=1)
w.mainloop()

Run this file as before and have a go at typing into it. You should see the line numbers change to always be in accordance with the amount of text in the accompanying Text widget.

With this class finished, we can begin using it within our TextEditor in a similar way to the Highlighter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset