使用PIL和tkinter动态调整图像大小



我想知道是否可以动态调整图像大小(保持其纵横比(。我做了一个图像查看器应用程序,但后来垂直长的图像溢出了屏幕,所以我想知道一种调整图像大小的方法,我尝试了一种方法,它包含在下面。但我仍然得到溢出屏幕的相同输出。

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

我仍然得到一个没有调整大小的图像,不知道为什么。

然后我尝试了这个方法,我设置了一个分辨率,它对我的屏幕很好。但如果我发送给其他人,它不会动态调整。

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中的照片应用程序一样。

完整代码:

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

不确定这是否是您想要的,但下面我定义了一个class,它总是将其图像width调整为950,并将高度调整为原始height*delta:

import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
class DynamicImage(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)
def resizing(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()

如果你稍微修改一下,并根据实际窗口值传递宽度/高度,它也可以收缩或增长。

给你。scale1.0或更低时,图像将始终适合其主图像。这个答案基于@HenryYik的答案,但通过添加scale参数和考虑每个方向溢出的逻辑,使其更加动态。此外,它不是基于窗口屏幕空间,而是基于主屏幕空间,并且在resizing中进行了考虑,而不是在__init__中。

其他更改:

  • 使用super()__init__的超类并不理想,因此该部分已更改为更严格的语法
  • 除非您的头脑中有一个所有kwargs的确切顺序的运行列表,否则对于每个小部件,您永远不会使用*args,因此它被省略了

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

相关内容

  • 没有找到相关文章

最新更新