Search code examples
pythonimagetkinterscaling

Resize an image without breaking the layout in tkinter


Ok, so i have a problem and tried way too long to fix it. I dont know how to resize an image without it breaking my layout. In case (1) it fills up the whole space thus destroying my layout and i think it is because i call the configure function in a function that is bound to the <Configure> event. In case (2) i can resize the image but i have a huge border around the scaled image which destroys the layout. I even tried to use a Canvas instead of the Label but just drawing the Canvas destroys the layout, because it takes up too much space and shifts everything to the right.

Ideas how to fix it?

from tkinter import *
from PIL import ImageTk, Image

global image_label, image_ref

def main():
    global image_label
    root = Tk()
    root.geometry("600x400")

    root.grid_rowconfigure(0, weight=1)
    root.grid_columnconfigure(0, weight=1)
    content_frame = Frame(root, bg="#FF0000")
    content_frame.grid(row=0, column=0, sticky="news")

    column_ratio = [10,25,10,45,10]
    row_ratio = [10,80,10]
    for col_idx in range(5):
        content_frame.grid_columnconfigure(col_idx, weight=column_ratio[col_idx])
    for row_idx in range(3):
        content_frame.grid_rowconfigure(row_idx, weight=row_ratio[row_idx])

    image_label = Label(content_frame, bg="#00FF00")
    image_label.grid(row=1, column=1, sticky="news")
    #image_label.bind("<Configure>", resize_configure)  # <--- (1)

    second_label = Label(content_frame, bg="#0000FF", text="Label on the right")
    second_label.grid(row=1, column=3, sticky="news")
    second_label.bind("<Button-1>", resize_click)       # <--- (2)


    root.mainloop()

def resize(size, label):
    global image_ref
    image = Image.open("test.png").resize(size, Image.ANTIALIAS)
    image_ref = ImageTk.PhotoImage(image)
    label.configure(image=image_ref)

def resize_configure(event):
    global image_label
    size = (event.width, event.height)
    resize(size, image_label)

def resize_click(event):
    global image_label
    image_label.update()
    size = (image_label.winfo_width(), image_label.winfo_height())
    resize(size, image_label)



if __name__ == "__main__":
    main()

Case 1 as an image

Case 2 as an image

How it should look like


Solution

  • You can use place() instead of grid():

    import tkinter as tk
    from PIL import Image, ImageTk
    
    base_img = Image.open('test.png')
    
    def on_label_resize(event):
        lbl = event.widget
        img = ImageTk.PhotoImage(base_img.resize((event.width, event.height)))
        lbl.config(image=img)
        lbl.image = img
    
    def main():
        root = tk.Tk()
        root.geometry('640x400')
    
        img_lbl = tk.Label(root, image=None, bg='#00ff00')
        img_lbl.place(relx=.1, rely=.1, relwidth=.25, relheight=.8)
        img_lbl.bind('<Configure>', on_label_resize)
    
        txt_lbl = tk.Label(root, text='Label on the right', bg='#0000ff')
        txt_lbl.place(relx=.45, rely=.1, relwidth=.45, relheight=.8)
    
        root.mainloop()
    
    if __name__ == '__main__':
        main()