0

I'm trying to make a program which will fit text into a rectangle (x by y) depending on the text, the font and the font size

Here is the code

def fit_text(screen, width, height, text, font):
    measure_frame = Frame(screen) # frame
    measure_frame.pack()
    measure_frame.pack_forget()
    measure = Label(measure_frame, font = font) # make a blank label
    measure.grid(row = 0, column = 0) # put it in the frame

    ##########################################################
    # make a certain number of lines
    ##########################################################

    words = text.split(" ")
    lines = []
    num = 0
    previous = 0
    while num <= len(words):                
        measure.config(text = " ".join(words[previous:num])) # change text
        line_width = measure.winfo_width() # get the width
        print(line_width)
        if line_width >= width: # if the line is now too long
            lines.append(" ".join(words[previous:num - 1])) # add the last vsion which wasn't too long
            previous = num - 1 # previous is now different
        num = num + 1 # next word
    lines.append(" ".join(words[previous:])) # add the rest of it
    return "\n".join(lines)

from tkinter import *    
window = Tk()
screen = Canvas(window)
screen.pack()
text = fit_text(screen, 200, 80, "i want to fit this text into a rectangle which is 200 pixels by 80 pixels", ("Purisa", 12))
screen.create_rectangle(100, 100, 300, 180)
screen.create_text(105, 105, text = text, font = ("Purisa", 12), anchor = "nw")

The problem with this is no matter what text is in the label the result from measure.winfo_width() is always 1. Here is where I found this from but it doesn't seem to work for me

Community
  • 1
  • 1
Tom Fuller
  • 5,291
  • 7
  • 33
  • 42

3 Answers3

1

The widget will not have a width until it is packed. You need to put the label into the frame, then pack it, then forget it.

J Earls
  • 1,792
  • 8
  • 12
  • nothing has changed, whether I pack it or not it still says line_width is 1, the label is also already in the frame – Tom Fuller Sep 03 '16 at 21:13
1

The problem with your code is that you're using the width of a widget, but the width will be 1 until the widget is actually laid out on the screen and made visible, since the actual width depends on a number of factors that aren't present until that happens.

You don't need to put the text in a widget in order to measure it. You can pass a string to font.measure() and it will return the amount of space required to render that string in the given font.

For python 3.x you can import the Font class like this:

from tkinter.font import Font

For python 2.x you import it from the tkFont module:

from tkFont import Font

You can then create an instance of Font so that you can get information about that font:

font = Font(family="Purisa", size=18)
length = font.measure("Hello, world")
print "result:", length

You can also get the height of a line in a given font with the font.metrics() method, giving it the argument "linespace":

height = font.metrics("linespace")
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
1

I've actually stumbled across a way of doing this through trial and error

By using measure.update_idletasks() it calculates the width properly and it works! Bryan Oakley definitely has a more efficient way of doing it though but I think this method will be useful in other situations

P.S. I wouldn't mind some votes to get a nice, shiny, bronze, self-learner badge ;)

Tom Fuller
  • 5,291
  • 7
  • 33
  • 42