Search code examples
pythonfontstkinterwidgetpixel

Fitting text into a rectangle (width x by height y) with tkinter


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


Solution

  • 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")