Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Text Widget size

0 views
Skip to first unread message

Salim Zayat

unread,
May 21, 2002, 11:07:10 AM5/21/02
to

Hey all. What's up? I have a quick question about the Tkinter Text
widget. If I have a string of text inserted, is there a way to change
the size of the Text widget to fit it perfectly, i.e. changing the height
and width to display all the lines of Text that are there and not have
any extra lines?

Salim

Eric Brunel

unread,
May 21, 2002, 2:27:05 PM5/21/02
to Salim Zayat

That shouldn't be too difficult: just create your text with the minimum
width and height (1 and 1...) and bind a callback to the <KeyRelease> event
getting the number of lines in the text and the length of the longest line
and set them respectively as the height and width of the Text object:

t = Text(..., width=1, height=1)
def resize(event):
lines = t.get(1.0, END).splitlines()
maxW = 0
if lines: maxW = max([len(l) for l in lines])
t.configure(width=maxW, height=len(lines))
t.bind("<KeyRelease>", resize)

Be warned: this doesn't work in all cases... If you do an t.insert or a
t.delete, you'll have to call the resize function manually. There are also
far more complex issues with non-default fonts, especially proportional
fonts, and tab management, that will make the simple method above miserably
fail... But you get the idea.

HTH
--
- Eric Brunel <eric....@pragmadev.com> -
PragmaDev : Real Time Software Development Tools - http://www.pragmadev.com

Jeff Epler

unread,
May 21, 2002, 12:44:00 PM5/21/02
to
I have not succeeded in solving this problem. The following function
comes close. However, Tk doesn't easily yield up the information
necessary.

The principle is to use the 'dlineinfo' command to get information about
each line in the text widget in turn, and then from that find out the
height and width of the entire window.

The required width is the maximum x coordinate returned by dlineinfo(),
and the required height is the sum of the required height for each line.
These numbers are in pixel units.

Then, since units for height= and width= of a text are in character
units, it is necessary to compute the number of pixels in the same way
that Tk computes it---from the width of the character 0, and the overall
height of the font. These are accessible via "font measure" and "font
metrics" commands, but don't seem to be directly available via the Tkinter
wrapper.

The 'update_idletasks' call is necessary to let the widget configure
itself. The height is set to 2 so that 'dlineinfo' returns the true
height of the line, and this may still be wrong if any single line is
taller than 2 default lines (probably easily true with embedded windows)
(put a number like 100 here instead?). This may cause the window to
"twitch" since it is resized twice during this process.

Good luck -- it's not a pretty corner of Tk that you find yourself in...

Jeff

#-----------------------------------------------------------------------
import Tkinter, math

def fittext(t):
t.configure(width=1, height=2, wrap="none")
t.update_idletasks()
end = int(float(t.index("end")))
x = y = 0
for line in range(0, end):
idx = "%d.0" % line
t.see(idx)
bb = t.dlineinfo(idx)
print bb, bb[3] - bb[1] + 1, bb[2] - bb[0] + 1
y = y + bb[3]
x = max(x, bb[2])
font = t.cget("font")
print t
h = int(t.tk.call("font", "measure", font, "0"))
v = int(t.tk.call("font", "metrics", font, "-linespace"))
print x, y, h, v
t.configure(width=int(math.ceil(x/h))+1, height=int(math.ceil(y/v)))
t.see("0.0")

# demo
t = Tkinter.Text()
t.insert("end", open("fittext.py").read())
t.tag_add("blah", 0.0, "end")
t.tag_configure("blah", font="helvetica -24")
t.pack()
fittext(t)
t.mainloop()


J.Jacob

unread,
May 22, 2002, 7:35:12 AM5/22/02
to
> [Salim Zayat]

Here is the method (called resize()) in class TextMap that does
what you asked for when called without parameters. class TextMap
is of type Tkinter.Frame and has a .text attribute of type
Tkinter.Text. Works with python 1.5.2 or later.

I think you can figure out how it is implemented now, for more
details: the class TextMap is in the textui module I submitted to
parnassus, you can also get it at
http://www.cwi.nl/~jacob/textui.py

def resize(self, height=None, width=None):
if not (height or width): # fit window to displayed
# text inside
textstr = self.gettext()
self.width = 0
self.height = 0
for s in string.split(textstr, '\n'):
self.height = self.height + 1
if len(s) > self.width: self.width = len(s)
self.resize(height=self.height, width=self.width)
else:
if height:
self.height = height
self.text.config(height=height)
if width:
self.width = width
self.text.config(width=width)
self.text.update()

def gettext(self):
"Return the data (string) in the window"
return self.text.get('1.0', END+'-1c') # first
# through last END points to just beyond the last
# character in the text string '-1c' because this
# widget adds a trailing newline char to its
# contents

0 new messages