Python Forum
tkinter text widget word wrap position
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
tkinter text widget word wrap position
#1
Hi,

I'm trying to figure out the index position on where the tkinter text widget cuts of the word when using WORD wrap. I thought the newline '\n' would indicate this, but I was wrong. If you see the example below (I replaced '\n' with '$'), you see the cut off word is not indicated by a '\n'.

Is there any way I can detect the index of the cut off position? I would like to use this to create a text editor with line numbers when using WORD wrap.

from tkinter import *
from decimal import Decimal

class app:
    def __init__(self, master):
        self.master = master

        master.title("PyEditor")
        master.geometry("720x180")

        master.grid_columnconfigure(1, weight=1)
        master.grid_rowconfigure(0, weight=1)

        self.lineframe = LineNumberFrame(master)
        self.lineframe.grid(row=0, column=0)
        
        self.edit_space = CustomText(master, wrap= WORD)
        self.edit_space.grid(row=0, column=1, sticky=NSEW)
        self.lineframe.attach(self.edit_space)
        self.edit_space.bind("<<TextModified>>", self._on_change)
        
        self.edit_scroll = Scrollbar(master, orient = VERTICAL, command = self._multi_scroll )
        
        self.edit_scroll.grid(row = 0, column = 2, sticky=NS)
        
        
        self.edit_space.configure(yscrollcommand = self._scroll1)
        self.lineframe.line_numbers.configure(yscrollcommand = self._scroll2)
        
        master.bind_all("<MouseWheel>", self._on_mousewheel)
        
    def _scroll1(self, *args):
        if self.lineframe.line_numbers.yview() != self.edit_space.yview():
            self.lineframe.line_numbers.yview_moveto(args[0])
            
        self.edit_scroll.set(*args)
        
    def _scroll2(self, *args):
        if self.edit_space.yview() != self.lineframe.line_numbers.yview():
            self.edit_space.yview_moveto(args[0])
            
        self.edit_scroll.set(*args)
        
    def _on_change(self, event):
        self.lineframe.redraw()
        
    def _multi_scroll(self, *args):
        self.edit_space.yview(*args)
        self.lineframe.line_numbers.yview(*args)
        
    
        
    def _on_mousewheel(self,event):
        self.edit_space.yview("scroll", -1 * (event.delta), "units")
        self.lineframe.line_numbers.yview("scroll", -1 * (event.delta), "units")

class LineNumberFrame(Frame):    
    def __init__(self, master: Tk = None):
        super().__init__(master, width = 1)
        self.master = master
        self.grid(row = 0, column = 0, sticky="nsew")
        self.create_widgets()
        
    def create_widgets(self):
        self.line_numbers = Text(self.master, width = 3)
        self.line_numbers.config(state = DISABLED)
        self.line_numbers.grid(row=0, column=0, sticky=NSEW)
        
    def attach(self, text_widget):
        self.text_widget = text_widget
        
    def redraw(self):
        self.line_numbers.config(state = NORMAL)
        self.line_numbers.delete("1.0", END)
        
                
        i = 1.0
        lastline = self.text_widget.index("end")
        
        is_first = True

        while True :
            if Decimal(i).compare(Decimal(lastline)) >= 0:
                break

            linenum = str(i).split(".")[0]          
            print("idx: " + self.text_widget.get(str(i) + " linestart", str(i) + " lineend+1c").replace("\n", "$"))

            if is_first == True:
                self.line_numbers.insert(END, linenum)
                is_first = False
            else:
                self.line_numbers.insert(END, "\n" + linenum)

            i = self.text_widget.index("%s+1line" % i)
            
        self.line_numbers.config(state = DISABLED)
        
        

class CustomText(Text):
    def __init__(self, *args, **kwargs):
        """A text widget that report on internal widget commands"""
        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, command, *args):
        cmd = (self._orig, command) + args
        result = self.tk.call(cmd)
        
       

        if command in ("insert", "delete", "replace"):
            self.event_generate("<<TextModified>>")

        return result
        
        

root = Tk()
my_gui = app(root)

# Insert some text
my_gui.edit_space.insert(END, "A very long line to see how the word wrap works. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.\n")

for x in range(2,10):   
    my_gui.edit_space.insert(END, "Line " + str(x) +"\n")

root.mainloop()
Thanks
Reply
#2
The text window described here has an expandable geometry, so the wrap position varies as the window is expanded and/or contracted.

There are some command attributes that have some control over padding (spacing1, spacing2 and spacing3), as well as some modifiers in the wrap command:
Quote:wrap
This option controls the display of lines that are too wide.
  • With the default behavior, wrap=tk.CHAR, any line that gets too long
    will be broken at any character.
  • Set wrap=tk.WORD and it will break the line after the last word that will
    fit.
  • If you want to be able to create lines that are too long to fit in the window,
    set wrap=tk.NONE and provide a horizontal scrollbar.

If you don't already have a copy, I recommend downloading John Shipman's tkinter manual here: https://reu.cct.lsu.edu/documents/Python...kinter.pdf

There are other commands that may help, starting on page 82
Reply
#3
(Mar-17-2021, 10:15 AM)Larz60+ Wrote: The text window described here has an expandable geometry, so the wrap position varies as the window is expanded and/or contracted.

There are some command attributes that have some control over padding (spacing1, spacing2 and spacing3), as well as some modifiers in the wrap command:
Quote:wrap
This option controls the display of lines that are too wide.
  • With the default behavior, wrap=tk.CHAR, any line that gets too long
    will be broken at any character.
  • Set wrap=tk.WORD and it will break the line after the last word that will
    fit.
  • If you want to be able to create lines that are too long to fit in the window,
    set wrap=tk.NONE and provide a horizontal scrollbar.

If you don't already have a copy, I recommend downloading John Shipman's tkinter manual here: https://reu.cct.lsu.edu/documents/Python...kinter.pdf

There are other commands that may help, starting on page 82

I've tried several methods, like 'dlineinfo' and 'bbox', but none of them provide me with the necessary info...
Reply
#4
I think dlineinfo provides what ypu need, the vertical position of each line, but you need to use place instead of grid to position the line numbers. Even without word wrap this would be necessary to support rich text.
Reply
#5
(Mar-17-2021, 08:45 PM)deanhystad Wrote: I think dlineinfo provides what ypu need, the vertical position of each line, but you need to use place instead of grid to position the line numbers. Even without word wrap this would be necessary to support rich text.

Thx, I was looking at the wrong attribute of dlineinfo.

A question though, what makes 'place' exactly better than 'grid' for the line numbers in this case?
Reply
#6
Grid does not let you specify location, only relative location. Line number 11 might be 13 or 26 pixels below line number 10 depending on if line 10 wraps. I suppose you could add an empty label to the line number grid, but you'd have to keep track of that somehow.
Reply
#7
Ok, but I'm using a text widget in which I display the line numbers. So that's probably my problem...

Thanks!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Problems trying to position images with Tkinter emont 3 754 Dec-12-2023, 07:20 AM
Last Post: menator01
  TKinter Widget Attribute and Method Quick Reference zunebuggy 3 847 Oct-15-2023, 05:49 PM
Last Post: zunebuggy
  [Tkinter] Updating tkinter text BliepMonster 5 5,949 Nov-28-2022, 01:42 AM
Last Post: deanhystad
  [Tkinter] The Text in the Label widget Tkinter cuts off the Long text in the view malmustafa 4 4,839 Jun-26-2022, 06:26 PM
Last Post: menator01
  Tkinter Exit Code based on Entry Widget Nu2Python 6 2,982 Oct-21-2021, 03:01 PM
Last Post: Nu2Python
  tkinter change the text of the checkbox zazas321 1 3,824 Sep-17-2021, 06:19 AM
Last Post: zazas321
  [Tkinter] Text widget inert mode on and off rfresh737 5 3,854 Apr-19-2021, 02:18 PM
Last Post: joe_momma
  Line numbers in Text widget rfresh737 3 5,399 Apr-15-2021, 12:30 PM
Last Post: rfresh737
  [Tkinter] tkinter.Menu – How to make text-variable? Sir 3 5,629 Mar-10-2021, 04:21 PM
Last Post: Sir
  tkinter python button position problem Nick_tkinter 3 3,557 Jan-31-2021, 05:15 AM
Last Post: deanhystad

Forum Jump:

User Panel Messages

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