Image distortion, rendering in both directions

I am following the tutorial for an image within a canvas to re-render regarding the canvas. But for the following if-else statement, it renders just for width (horizontally) motion but not for height (it goes beyond canvas). So I suspect I have to nest another condition for the height, then? What math is behind this code?

i

f canvas_ratio > image_ratio:
    height = int(event.height)
    width = int(height * image_ratio)
  else: 
    width = int(event.width)
    height = int(width / image_ratio)

The idea here is to maintain the image’s ratio.
If the window is wide then the height is the limit.
If the windows is tall then the width is the limit.

Thus an image that is 16:9 will not be distorted when drawn if the window is 20:9 or 16:11 for example.

Well, it doesnt work to me like that. So I am wondering if I still have an error in the code or if its a bug e.g. related to the fact I am on Linux. What I can se in GUI is:

  • If I minimize width, the image heigh gets shrinked also
  • If I minimize height the image width stays the same

Quick question: how do you set values for canvas_ratio and image_ratio?

def zobraz_cely_obrazek(event):
...
   canvas_ratio = event.width / event.height
...
canvas.bind("<Configure>", zobraz_cely_obrazek)
...
image = Image.open("/home/~/test_pillow.JPG")
#print(puvodni_obrazek)
image_ratio = image.size[0] / image.size[1]

OK, I have done some testing and I think:

should contain division not multiplication :thinking:

Strange, but no change on my site. Full code looks like this:

import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk

def resize_image(event): 
  global resized_tk
  
  canvas_ratio = event.width / event.height
  
  if canvas_ratio > image_ratio:
    height = int(event.height)
    width = int(height / image_ratio)
  else: 
    width = int(event.width)
    height = int(width / image_ratio)

    resized_image = puvodni_obrazek.resize((width, height))
    resized_tk = ImageTk.PhotoImage(resized_image)
    canvas.create_image(int(event.width / 2), int(event.height / 2), anchor = "center", image = resized_tk)
  
#Main window
window = tk.Tk()
window.geometry("600x400")
window.title("Obrázky")

window.columnconfigure((0,1,2,3), weight = 1, uniform = "a")
window.rowconfigure(0, weight = 1)

puvodni_obrazek = Image.open("/home/~/test_pillow.JPG")
image_ratio = puvodni_obrazek.size[0] / puvodni_obrazek.size[1]
image_tk = ImageTk.PhotoImage(puvodni_obrazek)


jiny_obrazek = Image.open("/home/~/kabrinec4d.jpg").resize((30,30))
jiny_obrazek_tk = ImageTk.PhotoImage(jiny_obrazek)

#Widgets
button_frame = ttk.Frame(window)

button = ttk.Button(button_frame, text = "Button 1", image = jiny_obrazek_tk, compound = "left")
button.pack(pady = 10)

button2 = ttk.Button(button_frame, text = "Button 2", image = jiny_obrazek_tk, compound = "left")
button2.pack(pady = 10)

button_frame.grid(column = 0, row = 0, sticky = "nsew")


canvas = tk.Canvas(window, background = "black", bd = 0, highlightthickness = 0, relief = "ridge")
canvas.grid(column = 1, columnspan = 3, row = 0, sticky = "nsew")

canvas.bind("<Configure>", resize_image)

window.mainloop()

The expected behavior is as follows:

  • when the window is reduced horizontally, the height of the image is reduced
  • shrinking the window vertically reduces the width of the image

Current behavior:

  • when the window is reduced horizontally, the height of the image is reduced
  • when shrinking the window vertically, the image remains the same width

Here is the problem! the lines starting with resized_image... are indented, so they belongs to the else: branch. In other words: resizing will be done only when the if-clause is False. The solution is simple: remove extra indent, so it will be in line with the rest of the function. Like this:

def resize_image(event): 
  global resized_tk
  
  canvas_ratio = event.width / event.height
  
  if canvas_ratio > image_ratio:
    height = int(event.height)
    width = int(height / image_ratio)
  else: 
    width = int(event.width)
    height = int(width / image_ratio)

  resized_image = puvodni_obrazek.resize((width, height))
  resized_tk = ImageTk.PhotoImage(resized_image)
  canvas.create_image(int(event.width / 2), int(event.height / 2), anchor = "center", image = resized_tk)
2 Likes

After fixing that, I think you may find that the * you changed to a / should indeed have been * after all. And perhaps you will consider switching to the community standard 4-space indents :wink: Many years ago, I used to use 2-space indents as well, because they felt more “square”. But the extra visual distinction does seem to help.

1 Like

Cool, thx.

Nice. Well regarding the indent, its generated by the IDE I am using and it seems to me, that with multiple operations with a file it is not able to handle it properly, so once Ill have a time, Ill create dualboot and probably get Sublime text instead.

Whatever IDE you use, there should be an option in preferences to change the indent width. (Sometimes it may guess wrong, about how many indents you want. But you can normally fix that with tab and backspace keys. If it’s properly configured, then backspace at the beginning of a line should erase an entire indent, and tab should convert to spaces automatically.)

Also, Sublime Text is available for all platforms (Windows/Mac/Linux).

That is odd folder name “~” to use. On Unix systems by convention ~ is used by the shell as a short hand for $HOME.

It will work, but it is likely to lead to confusing situations for you.

Or did you mean to write ~/test_pillow.JPG and have the ~ replaced with $HOME? If so you will need to handle the expansion of the ~ by using pathlib.Path object or os.path functions.

For example:

>>> import pathlib
>>> fn = pathlib.Path('~/.bashrc')
>>> print(fn)
~/.bashrc
>>> print(fn.expanduser())
/Users/barry/.bashrc
>>>

Rstudio. Yes, there is an option to highlight hidden characters, but I am straggling to change its color, so I guess, better editor would be needed anyway.

I dont think so. I was allready investigating that and there was a problem with Linux, which I allready doesnt rember. Something like the features which works for Mac and Windoes, doesnt work on Linux.

But dualboot is not a big deal, I still need it because of other apps, which are not designed for Linux.

No, that is not a folder name. That is the path, which I dont want to disclose publically.

So to wrap it up, the you would describe the change of logic between these two scripts, that in the first case the sides of the image are locked, but they go beyond canvas:

if canvas_ratio > image_ratio:
    width = int(event.width)
    height = int(width / image_ratio)
  else: 
    height = int(event.height)
    width = int(height * image_ratio)

And in this case the sides are locked and more over they doesnt go beyond canvas:

if canvas_ratio > image_ratio:
    height = int(event.height)
    width = int(height * image_ratio)
  else: 
    width = int(event.width)
    height = int(width / image_ratio)

It seems to me, that defining width first must be the same as defining height first.