0

I have text box my_text on gui at location

my_text = Text(root, width=98, height=35, font=("Helvetica", 10), bg="cyan", fg="black")
my_text.grid(row=4, column=0, padx=(20, 50), pady=(20, 0), rowspan=3, sticky="e")

I want to add lines numbers to this text box using Bryan Oakley code here.

My code:

import tkinter as tk
from tkinter import *

root = Tk()

my_text = Text(root, width=98, height=35, font=("Helvetica", 10), bg="cyan", fg="black")

my_text.grid(row=4, column=0, padx=(20, 50), pady=(20, 0), rowspan=3, sticky="e")


text_file = open("sample.xml", 'r')
s = text_file.read()

my_text.delete("1.0", "end")

my_text.insert(END, s)

class TextLineNumbers(tk.Canvas):
    def __init__(self, *args, **kwargs):
        tk.Canvas.__init__(self, *args, **kwargs)
        self.root = Tk()
        self.textwidget = None

    def attach(self, text_widget):
        self.textwidget = text_widget
        
    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]
            linenum = str(i).split(".")[0]
            self.create_text(2,y,anchor="nw", text=linenum)
            i = self.textwidget.index("%s+1line" % i)
    def run(self):
        self.root.mainloop()


linenos = TextLineNumbers()
linenos.attach(my_text)
linenos.redraw()
root.mainloop()

Code is not displaying line number. It is displaying just text. How to display linenumbers in text box? Thanks in advance.

enter image description here

Ashok Anumula
  • 1,338
  • 1
  • 4
  • 9
  • Does [this youtube video](https://youtu.be/m2VCivF1y34) help you? – Swagrim Dec 19 '21 at 10:54
  • @Swagrim I want to make above code work. Any help will be greately appreciated. – Ashok Anumula Dec 19 '21 at 11:05
  • you have few mistakes. You have two `tk.Tk()`. You have two `mainloop()`. You have to put `TextLineNumbers` in window - using `grid()` – furas Dec 19 '21 at 12:07
  • @furas can you pls post updated code? I am new to python and tkinter – Ashok Anumula Dec 19 '21 at 12:22
  • you have to add `linenos.grid(row=4, column=0)` to show it. And you have to move `Text` to `column=1`. But it seems `dlineinfo` doesn't work as it expected - at least on my Linux Mint - because it gives values only for first line. – furas Dec 19 '21 at 12:25
  • because `dlineinfo` doesn't work on my computer so I would use second `Text` and simply split text on `new line` - `lines = s.split("\n")` - to get number of lines - `number = len(lines)` - and use `for`-loop to put in this new Text numbers in `range(1, number+1)` – furas Dec 19 '21 at 12:30

1 Answers1

0

You mentioned this great example, why not just modify it to suit your needs? I was curious and modified the Example class from given link by adding a button to call a function load_xml which loads files via filechooser, deletes the previous data in the CustomText widget and inserts the new data:

import tkinter as tk
from tkinter import filedialog
import os


class TextLineNumbers(tk.Canvas):
    def __init__(self, *args, **kwargs):
        tk.Canvas.__init__(self, *args, **kwargs)
        self.textwidget = None

    def attach(self, text_widget):
        self.textwidget = text_widget

    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]
            linenum = str(i).split(".")[0]
            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 = self.tk.call(cmd)

        # generate an event if something was added or deleted,
        # or the cursor position changed
        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")

        # return what the actual widget returned
        return result


class Example(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        self.text = CustomText(self)
        self.vsb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
        self.text.configure(yscrollcommand=self.vsb.set)
        self.text.tag_configure("bigfont", font=("Helvetica", "24", "bold"))
        self.linenumbers = TextLineNumbers(self, width=30)
        self.linenumbers.attach(self.text)

        self.vsb.pack(side="right", fill="y")
        self.linenumbers.pack(side="left", fill="y")
        self.text.pack(side="right", fill="both", expand=True)

        # new button to call load_xml and show status
        self.load_button = tk.Button(root, text="Load file", command=self.load_xml)
        self.load_button.pack(side="top")

        self.text.bind("<<Change>>", self._on_change)
        self.text.bind("<Configure>", self._on_change)

        self.text.insert("end", "one\ntwo\nthree\n")
        self.text.insert("end", "four\n", ("bigfont",))
        self.text.insert("end", "five\n")

    def _on_change(self, event):
        self.linenumbers.redraw()

    def load_xml(self):
        """Load any file, delete current text and insert new data"""
        input_file = filedialog.askopenfilename(title="Load a textfile",
                                                filetypes=(("XML", "*.xml"),
                                                           ("Text", "*.txt"),
                                                           ("All files", "*.*")),
                                                initialdir=os.getcwd())

        if input_file:
            self.text.delete("1.0", "end")
            self.load_button.config(text=f"Currently loaded: {input_file.split(os.sep)[-1]}")
            with open(input_file, 'r') as f:
                self.text.insert("end", f.read())


if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

mnikley
  • 1,625
  • 1
  • 8
  • 21