使用PIL和Tkinter动态调整图像大小 [英] Dynamic resizing of image using PIL and tkinter

查看:92
本文介绍了使用PIL和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 screen, so i wanted to know a method to resize image, ive tried a way and its included below. But still im getting the same output that overflows the screen.

from win32api import GetSystemMetrics
from tkinter import *

screen_width, screen_height = GetSystemMetrics(0), GetSystemMetrics(1)

root = Tk() # this is your window
root.geometry("{}x{}".format(screen_width//2, screen_height//2)) # set size of you window here is example for 1/2 screen height and width

img = Image.open("picture_name.png")
width, height = screen_width//4, screen_height//4 

img.resize((width, height), Image.ANTIALIAS) 

l = Label(root,image=img)
l.pack()

root.mainloop()

仍然无法获得未调整大小的图像,不知道为什么.

Still im getting an image that is not resized, dont know why.

然后我尝试了这种方法,在其中设置了分辨率,并且该方法在我的屏幕上正常工作.但是如果我要发送给其他人,它将不会动态调整.

Then i tried out this method, where I set a resolution, and it works fine for my screen. but if i were to send to someother people, it would not dynamically adjust.

desired_size = 950

im = Image.open('img.png')
old_size = im.size

ratio = float(desired_size)/max(old_size)
new_size = tuple([int(x*ratio) for x in old_size])

im = im.resize(new_size, Image.ANTIALIAS)
img = ImageTk.PhotoImage(im)
l = Label(root, image=img)
l.image = img
l.pack()

我想知道一种能动态调整图像大小并保持其宽高比的方法,因此不会发生失真,就像Windows 10中的照片"应用程序一样.

I would like to know a way to dynamically resize the image maintaining its aspect ratio as well, so no distortion takes place, like the Photos app in Windows 10.

整个代码:

from tkinter import *
from tkinter import messagebox
from glob import glob
from tkinter import filedialog
from PIL import Image, ImageTk

root = Tk()
root.config(bg='white')
root.title('Image Viewer App')

def forward_image(event=None):
    global n
    n += 1
    if n > len(main_img)-2:
        forward['state'] = DISABLED
        root.unbind('<Key-Right>')

    else:
        backward['state'] = NORMAL
        root.bind('<Key-Left>', backward_image)

    im = Image.open(main_img[n])
    old_size = im.size

    ratio = float(desired_size)/max(old_size)
    new_size = tuple([int(x*ratio) for x in old_size])

    im = im.resize(new_size, Image.ANTIALIAS)
    img = ImageTk.PhotoImage(im)
    l.image = img
    l.config(image=img)
    status.config(text=f'{n+1} of {total} images')

def backward_image(event=None):
    global n
    n -= 1
    if n <= 0:
        backward['state'] = DISABLED
        root.unbind('<Key-Left>')

    else:
        forward['state'] = NORMAL
        root.bind('<Key-Right>', forward_image)

    im = Image.open(main_img[n])
    old_size = im.size

    ratio = float(desired_size)/max(old_size)
    new_size = tuple([int(x*ratio) for x in old_size])

    im = im.resize(new_size, Image.ANTIALIAS)
    img = ImageTk.PhotoImage(im)
    l.image = img
    l.config(image=img)
    status.config(text=f'{n+1} of {total} images')

def path():
    global main_img
    path = filedialog.askdirectory(
        initialdir='c:/', title='Select a folder with images')
    img_png = glob(path+'/*.png')
    img_jpg = glob(path+'/*.jpg')
    main_img = img_jpg + img_png


path()

n = 0
desired_size = 950

im = Image.open(main_img[n])
old_size = im.size

ratio = float(desired_size)/max(old_size)
new_size = tuple([int(x*ratio) for x in old_size])

im = im.resize(new_size, Image.ANTIALIAS)
img = ImageTk.PhotoImage(im)
l = Label(root, image=img)
l.image = img
l.pack()


forward = Button(root, text='Forward', command=forward_image)
forward.pack(side=RIGHT)

backward = Button(root, text='Backward', command=backward_image)
backward.pack(side=LEFT)
backward['state'] = DISABLED

total = len(main_img)
status = Label(root,text=f'{n+1} of {total} images',bg='white',font=('helvetica',10))
status.pack(side=BOTTOM)

root.focus_force()

root.bind('<Key-Left>', backward_image)
root.bind('<Key-Right>', forward_image)
root.bind('<Escape>', lambda event: root.state('normal'))
root.bind('<F11>', lambda event: root.state('zoomed'))

if total <= 1:
    backward['state'] = DISABLED
    forward['state'] = DISABLED
    root.unbind('<Key-Right>')
    root.unbind('<Key-Left>')

if total == 0:
    messagebox.showerror('No image','Choose a directory with images.')
root.mainloop()

先谢谢您了:D

推荐答案

去吧.如果 scale 1.0 或更低,图像将始终适合其主图像.该答案基于@HenryYik答案,但通过添加 scale 参数以及考虑各个方向上的溢出的逻辑,它变得更加动态.而且,不是基于窗口屏幕空间,而是基于主屏幕空间,并且考虑是在 resize 中进行考虑的,而不是在 __ init __ 中进行.

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__.

其他更改:

  • super()用作 __ init __ 超类并不理想,因此该部分已更改为更严格的语法.
  • 除非您在所有 kwargs 的确切顺序的开头都有一个运行列表,否则对于每个小部件,您将永远不会使用 * args ,因此已将其省略
  • 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'))
    

class Slide(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   = None if not image_path else Image.open(image_path)
        self.p_img = None
        self.scale = scale
                
        self.bind("<Configure>", self.resizing)
        
    def set_image(self, image_path:str):
        self.img   = Image.open(image_path)
        self.resizing()

    def resizing(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) > 1 else 1
                iw, ih = mw*r, ih*r
            else:
                iw = iw*(mh/ih)
                r = mw/iw if (iw/mw) > 1 else 1
                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 = 0

def get_slides():
    global total
    path  = filedialog.askdirectory(initialdir='c:/', title='Select a folder with images')
    cache = glob(path+'/*.png') + glob(path+'/*.jpg')
    
    total = len(cache)
    if not 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()


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

    
def next_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)


def previous_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()

这篇关于使用PIL和Tkinter动态调整图像大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆