Skip to content Skip to sidebar Skip to footer

Dynamic Resizing Of Image Using Pil And Tkinter

I wanted to know if it was possible to resize the image dynamically(maintaining its aspect ratio). I made an image viewer app, but then the vertically long images overflow the scre

Solution 1:

Not sure if this is what you want, but below I defined a class that always resize its image width to 950, and height to original height*delta:

import tkinter as tk
from PIL import Image, ImageTk

root = tk.Tk()

classDynamicImage(tk.Label):
    def__init__(self, master=None, image_path="", *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.width = master.winfo_screenwidth()//2
        self.height = master.winfo_screenheight()//2
        self.img = Image.open(image_path)
        self.p_img = None
        self.bind("<Configure>", self.resizing)

    defresizing(self, event=None):
        w, h = self.img.width, self.img.height
        if w>h:
            delta = self.width/w
            new_width, new_height = self.width, int(h*delta)
        else:
            delta = self.height/h
            new_width, new_height = int(w*delta), self.height
        self.p_img = ImageTk.PhotoImage(self.img.resize((new_width, new_height)))
        self.config(image=self.p_img)
        s.config(text=f"Dimension: {self.p_img.width()}x{self.p_img.height()}")

s = tk.Label(text="")
s.pack()

DynamicImage(root, image_path="your_path").pack(fill="both",expand=True)

root.mainloop()

If you modifty it a bit and pass the width/height according to actual window value, it can shrink or grow as well.

Solution 2:

Here ya' go. With a scale of 1.0 or lower the image will always fit in it's master. This answer is based on @HenryYik answer, but made more dynamic through the addition of the scale argument, and the logic to consider overflow in every direction. Also, instead of being based on window screenspace it's based on master screenspace, and that consideration is made in resizing, as opposed to in __init__.

other changes:

  • Using super() to __init__ a superclass is not ideal, so that part has been changed to a more strict syntax.
  • Unless you have a running list in your head of the exact order of all kwargs, for every widget, you will never use *args, so it has been omitted.
import tkinter as tk
from tkinter import messagebox, filedialog
from glob import glob
from PIL import Image, ImageTk

#configure root
root = tk.Tk()
root.title('Image Viewer App')
root.geometry('800x600')
root.config(bg='#222222',bd=0,padx=0,pady=0,highlightthickness=0)
root.bind('<Escape>', lambda event: root.state('normal'))
root.bind('<F11>', lambda event: root.state('zoomed'))
    

classSlide(tk.Label):
    def__init__(self, master, image_path:str='', scale:float=1.0, **kwargs):
        tk.Label.__init__(self, master, **kwargs)
        self.configure(bg=master['bg'])
        self.img   = Noneifnot image_path else Image.open(image_path)
        self.p_img = None
        self.scale = scale
                
        self.bind("<Configure>", self.resizing)
        
    defset_image(self, image_path:str):
        self.img   = Image.open(image_path)
        self.resizing()

    defresizing(self, event=None):
        if self.img:
            iw, ih  = self.img.width, self.img.height
            mw, mh  = self.master.winfo_width(), self.master.winfo_height()
            
            if iw>ih:
                ih = ih*(mw/iw)
                r = mh/ih if (ih/mh) > 1else1
                iw, ih = mw*r, ih*r
            else:
                iw = iw*(mh/ih)
                r = mw/iw if (iw/mw) > 1else1
                iw, ih = iw*r, mh*r
                
            self.p_img = ImageTk.PhotoImage(self.img.resize((int(iw*self.scale), int(ih*self.scale))))
            self.config(image=self.p_img)



total     = 0
slide_num = 0defget_slides():
    global total
    path  = filedialog.askdirectory(initialdir='c:/', title='Select a folder with images')
    cache = glob(path+'/*.png') + glob(path+'/*.jpg')
    
    total = len(cache)
    ifnot total:
        m = messagebox.askyesno('No Images','The directory you have chosen does not contain any images. Try Again?')
        if m:
            return get_slides()
        else:
            root.quit()
            exit(0)
        
    return cache


image_cache = get_slides()


defcommit_slide(n, t):
    slide.set_image(image_cache[n])
    status.config(text=f'{n+1} of {t} images')

    
defnext_slide(event=None):
    global slide_num, total
    slide_num = (slide_num+1)%len(image_cache)       #wrap
    commit_slide(slide_num, total)
    
root.bind('<Key-Right>', next_slide)


defprevious_slide(event=None):
    global slide_num, total
    slide_num = range(len(image_cache))[slide_num-1] #wrap
    commit_slide(slide_num, total)
    
root.bind('<Key-Left>', previous_slide)


#init display widgets
slide = Slide(root)
slide.pack()

tk.Button(root, text='prev', command=previous_slide).place(relx=.02, rely=.99, anchor='sw')
tk.Button(root, text='next', command=next_slide).place(relx=.98, rely=.99, anchor='se')

status = tk.Label(root, bg='white', font=('helvetica',10))
status.place(relx=.5, rely=.99, anchor='s')

#init first slide
commit_slide(slide_num, total)

root.focus_force()
root.mainloop()

Post a Comment for "Dynamic Resizing Of Image Using Pil And Tkinter"