2

I have been attempting to create a GUI with sliders and buttons with an output image to the right (NOT background image). A requirement is for only the image to dynamically change size based on the window size.

In my attempt the window properly displays a resized image only when resizing horizontally, not vertically. and when the window is updated after a vertical resize it will improperly scale the image, resulting in part of the image being cut off.

I am not looking to maintain a window/screen ratio like: -Dynamically resize images in Tkinter

I have used information from:

And youtube videos from codemy like How To Resize A Window Dynamically - Python Tkinter GUI Tutorial #80

to get this code:

import tkinter as tk
from tkinter import *
from tkinter.filedialog import askopenfilename
from PIL import ImageTk, Image


def show_values():
    print(slider1.get())


window = tk.Tk()

filename = askopenfilename()  # show an "Open" dialog box and return the path to the selected file

image = ImageTk.PhotoImage(Image.open(filename))
imageWidth = image.width()
imageHeight = image.height()

canvas = Canvas(window, width=imageWidth, height=imageHeight)
canvas.pack(fill=BOTH, expand=True, side=RIGHT)
canvas.create_image(0, 0, image=image, anchor='nw')

slider1 = Scale(window, from_=0, to=42, orient='vertical')
slider1.pack(side=LEFT)


def resizer(e):
    global image1, resized_image, new_image
    image1 = Image.open(filename)
    resized_image = image1.resize((e.width, e.height), Image.Resampling.LANCZOS)
    new_image = ImageTk.PhotoImage(resized_image)
    canvas.create_image(0, 0, image=new_image, anchor='nw')


window.bind('<Configure>', resizer)

Button(window, text='Process Image', command=show_values).pack(side=BOTTOM)

window.mainloop()

I have also tried to use a weighted grid and the NSEW (N+S+E+W) method to no success.

  • 1
    Have you verified that `e.width` and `e.height` are what you're expecting it to be? If so, it seems you're suggesting that `image1.resize(...)` is failing to resize the image. Is that the issue? Have you tried saving the resulting image to disk, and examining it outside of tkinter to see if it was resized properly? – Bryan Oakley Dec 13 '22 at 20:05
  • 1
    Also, are you aware that binding to the root window will also trigger the same event for every other widget? You seem to be assuming that `e.width` and `e.height` are the width and height of the root window, but it might not always be. – Bryan Oakley Dec 13 '22 at 20:06
  • My e.width and e.height change to values they are not supposed to be only when the window is changed in the vertical direction. Not even values that make much sense. With a 300x240 input image the e.width=87 and the e.height=26 which is inconsistent scaling. I will try to use winfo_width() and winfo_height() to solve this issue – Luke-McDevitt Dec 13 '22 at 20:33

1 Answers1

1

When you bind to a root window, that binding is applied to all widgets. This is due to the fact that bindings are applied to binding tags, and the tag for the root window is added to the list of bind tags for every widget.

Because of this, your resizer is being called once for the root window, and once for the canvas, every time the window changes size. That means that e.width and e.height will be different since sometimes it refers to the width of the window and sometimes to the width of the canvas.

The simple solution is to bind to the <Configure> event of only the canvas. If you have your canvas properly configured to resize when the window resizes, then this will all work just fine.

Also, you shouldn't be creating new image objects on the canvas. You're just stacking one image on top of another. Your program would eventually crash because of the multitude of images.

Instead, you can reconfigure the existing image object to use your new widget.

...
image_id = canvas.create_image(0, 0, image=image, anchor='nw')
...
def resizer(e):
    global image1, resized_image, new_image
    image1 = Image.open(filename)
    resized_image = image1.resize((e.width, e.height), Image.Resampling.LANCZOS)
    new_image = ImageTk.PhotoImage(resized_image)
    canvas.itemconfigure(image_id, image=new_image)

canvas.bind('<Configure>', resizer)
...
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685