简体   繁体   中英

line number spacing in text widget Tkinter

I am adding line number feature to the text widget by following Bryan Oakley's code here . I am inserting my own XML file in that text box. That file is listed below:

myFile.xml

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>

<p1:sample1 xmlns:p1="http://www.example.org/eHorizon">

   <p1:time nTimestamp="5">
      <p1:location hours = "1" path = '1'>
         <p1:feature color="6" type="a">560</p1:feature>
         <p1:feature color="2" type="a">564</p1:feature>
         <p1:feature color="3" type="b">570</p1:feature>
         <p1:feature color="4" type="c">570</p1:feature>
      </p1:location>
   </p1:time>

   <p1:time nTimestamp="6">
      <p1:location hours = "1" path = '1'>
         <p1:feature color="2" type="a">564</p1:feature>
         <p1:feature color="3" type="b">570</p1:feature>
         <p1:feature color="4" type="c">570</p1:feature>
      </p1:location>
   </p1:time>

</p1:sample1>

myCode.py

import Tkinter as tk

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)

        self.tk.eval('''
            proc widget_proxy {widget widget_command args} {

                # call the real tk widget command with the real args
                set result [uplevel [linsert $args 0 $widget_command]]

                # generate the event for certain types of commands
                if {([lindex $args 0] in {insert replace delete}) ||
                    ([lrange $args 0 2] == {mark set insert}) || 
                    ([lrange $args 0 1] == {xview moveto}) ||
                    ([lrange $args 0 1] == {xview scroll}) ||
                    ([lrange $args 0 1] == {yview moveto}) ||
                    ([lrange $args 0 1] == {yview scroll})} {

                    event generate  $widget <<Change>> -when tail
                }

                # return the result from the real widget command
                return $result
            }
            ''')
        self.tk.eval('''
            rename {widget} _{widget}
            interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
        '''.format(widget=str(self)))

class Example(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        self.text = CustomText(self)
        self.vsb = tk.Scrollbar(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)

        self.text.bind("<<Change>>", self._on_change)
        self.text.bind("<Configure>", self._on_change)
        file = open('C:/your/file/location/myFile.xml')
        self.text.insert("end", file.read())


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

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

What I want:

The result generated on running code looks like this:

在此输入图像描述

I have edited this pic using ms-paint to show red markings. Numbering of lines in red is what I want. As it can be seen that numbering on canvas also includes space, I want to modify code in such a manner that there are no line numbers on empty or white spaces.

So there are two objectives I want to achieve:

1). Follow line numbering in a way numbers marked in red are given.

2). After this changes are made if possible I also want to extract the line number from canvas for any given string for that text widget.

What I tried

I am trying to compare two files and introduce empty lines on places where there are missing points. Before I proceed I need to implement an algorithm which can exclude empty white space from allotting line numbers (which happens with most of the editors). So far I am playing around but cannot find how can I implement this in code above.

Hopefully, Bryan Oakley will see your question and post an eloquent solution. But in the mean time, this somewhat hacky approach works. :)

On each redraw, we grab the current text contents of the widget, and build a dictionary that uses the index of each non-blank line as the key in a dictionary that holds the actual line numbers we want.

def redraw(self, *args):
    '''redraw line numbers'''
    self.delete("all")

    # Build dict to convert line index to line number
    linenums = {}
    num = 1
    contents = self.textwidget.get("1.0", tk.END)
    for i, line in enumerate(contents.splitlines(), 1):
        i = str(i) + '.0'
        # Only increment line number if the line's not blank
        linetext = self.textwidget.get(i, "%s+1line" % i)
        if linetext.strip():
            linenums[i] = str(num)
            #print num, linetext,
            num += 1

    i = self.textwidget.index("@0,0")
    while True :
        dline = self.textwidget.dlineinfo(i)
        if dline is None: 
            break

        linenum = linenums.get(i)
        if linenum is not None:
            y = dline[1]
            self.create_text(2,y,anchor="nw", text=linenum)

        i = self.textwidget.index("%s+1line" % i)

If you un-comment the #print num, linetext, line it will print each non-blank line with its number on every redraw. You probably don't want that, but it should help you to figure out how to extract the line numbers for a given line.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM