5

I'd like to know how to get the number of lines in a Tkinter Text widget that has word wrap enabled.

In this example, there are 3 lines in the text widget :

from Tkinter import *

root = Tk()
text = Text(root, width = 12, height = 5, wrap = WORD)
text.insert(END, 'This is an example text.')
text.pack()

root.mainloop()

But methods that work for non-wrapped text, like :

int(text_widget.index('end-1c').split('.')[0]) 

will return 1 instead of 3. Is there another method that would properly count wrapped lines (and return 3 in my example) ?

Thanks for your help !

Xoot
  • 65
  • 7
  • It works as expected. You have only one line of text. Wrapping doesn't matter. http://stackoverflow.com/questions/21420258/tk-text-widget-index-not-properly-counting-lines – furas Nov 18 '15 at 17:48
  • see also "Missing `count` method in Tkinter's Text widget" http://stackoverflow.com/questions/33784637/missing-count-method-in-tkinters-text-widget – furas Nov 18 '15 at 17:52
  • @furas I agree, I did some bad phrasing here, I've just updated my question. Thanks for your link by the way ! – Xoot Nov 18 '15 at 18:11

2 Answers2

4

Working example using "missing count method"

It prints

displaylines: 3
lines: 1

from Tkinter import *

def count_monkeypatch(self, index1, index2, *args):
    args = [self._w, "count"] + ["-" + arg for arg in args] + [index1, index2]

    result = self.tk.call(*args)
    return result

Text.count = count_monkeypatch


root = Tk()
text = Text(root, width = 12, height = 5, wrap = WORD)
text.insert(END, 'This is an example text.')
text.pack()

def test(event):
    print "displaylines:", text.count("1.0", "end", "displaylines")
    print "lines:", text.count("1.0", "end", "lines")

text.bind('<Map>', test)

root.mainloop()

or with Button in place of bind

from Tkinter import *

#-------------------------------------------

def count_monkeypatch(self, index1, index2, *args):
    args = [self._w, "count"] + ["-" + arg for arg in args] + [index1, index2]

    result = self.tk.call(*args)
    return result

Text.count = count_monkeypatch

#-------------------------------------------

def test(): # without "event"
    print "displaylines:", text.count("1.0", "end", "displaylines")
    print "lines:", text.count("1.0", "end", "lines")

#-------------------------------------------

root = Tk()
text = Text(root, width = 12, height = 5, wrap = WORD)
text.insert(END, 'This is an example text.')
text.pack()

Button(root, text="Count", command=test).pack()

root.mainloop()
furas
  • 134,197
  • 12
  • 106
  • 148
  • 2
    In new versions of Tkinter `t.count('1.0', 'end', 'displaylines')` works without monkeypatching. – Андрей Беньковский Jul 03 '16 at 20:35
  • For some reason if I use the relevant line `t.count('1.0', tk.END, 'displaylines')` bound to a key, it works, but if I just use it in my main program, e.g. printing it, I get a totally different value. Why is that so? – zwiebel Nov 08 '21 at 17:46
  • 1
    @zwiebel I can't see your code - maybe you run it in wrong moment (when `Text` has different number of lines then you expect). People often make one mistake - they think that `GUI` works like `input()` - and they expect that it creates `Text` at once - but it is not true. True is that `mainloop` starts program and it creates window and add widgets to this window and everything before `mainloop` is executed before it shows windows - and when widgets are empty. – furas Nov 09 '21 at 11:22
  • @furas thanks for the hint. I will see, if that fixes the problem. If not, I was planning on posting a separate question with the relevant sample code! – zwiebel Nov 10 '21 at 12:22
  • @furas That was the key. If I do, for example `root.update()` and `root.update_idletasks()` before the count, I get the correct numbers... – zwiebel Nov 10 '21 at 14:19
  • it only shows that you run it before `mainloop()` because `root.update()` force `mainloop` to run one loop and update widgets. Other method is to use `root.after(100, function)` to run function (which count it) 100ms after starting `mainloop()` and then it should have all values in widgets. – furas Nov 10 '21 at 14:27
0

I know this is very old but, I wanted to throw a possible current(2022) answer for those working with wrapping lines on windows 10.

import tkinter as tk


def inspect_wrapline(event=None):
  start = f"{tk.INSERT} linestart"
  end   = f"{tk.INSERT} lineend"
  counter = event.widget.count(start, end, "displaylines")
  
  print("number of lines after first line", counter)


  start = f"{tk.INSERT} display linestart"
  end   = f"{tk.INSERT} display lineend +1c"
  dline = event.widget.get(start, end)

  print("Text on the displayline", dline)


root_window = tk.Tk()
text_widget = tk.Text(root_window)
text_widget.pack()
text_widget.insert("This is an example text.")
text_widget.bind("<Control-l>", inspect_wrapline)

root_window.mainloop()

Output:

insert @ line 0: (2,) This is an

insert @ line 1: (2,) example

insert @ line 2: (2,) text.


Terminology:

Displayline: a single line of text from left to right in the widget viewing area.

Logic line: a string of text regardless of wraps. There is only 1 'logic line' in a line that wraps (or at least that is my understanding.).


Note:

Even though the option is a "displayline" it will yield a displayline even if it is not currently visible in the viewing area.


Hope this helps some wayward tkinter users trying to figure out wrapping lines.

Valthalin
  • 415
  • 2
  • 7