标签中的 Python PIL 图像自动调整大小 [英] Python PIL Image in Label auto resize

查看:89
本文介绍了标签中的 Python PIL 图像自动调整大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个小部件来保存一个图像,该图像将自动调整大小以适应其容器,例如如果直接打包到窗口中,则扩展该窗口将扩展图像.

I'm trying to make a widget to hold an image that will automatically resize to fit its container, e.g. if packed directly into a window, then expanding that window will expand the image.

我有一些半函数式代码,但我不得不在其中一个例程中添加几个常量,以防止自动调整大小重新触发自身(导致其大小不断增长)

I have some code that is semi functional but I've had to add a couple of constants into one of the routines to prevent the auto resize from re triggering itself (causing it to keep growing in size)

我确定这是由于小部件的内部填充/边框造成的,但即使考虑到这一点,我也遇到了这个问题.

I'm sure that the reason for this is due to the widgets internal padding/border, but even trying to take that into account I get this issue.

我在 64 位 Windows 7 上使用 python 3.3.2 和 PIL 1.1.7我的代码如下:

I'm using python 3.3.2, and PIL 1.1.7 on 64 bit Windows 7 my code is the following:

from tkinter import tix
from PIL import Image, ImageTk

def Resize_Image(image, maxsize):
    r1 = image.size[0]/maxsize[0] # width ratio
    r2 = image.size[1]/maxsize[1] # height ratio
    ratio = max(r1, r2)
    newsize = (int(image.size[0]/ratio), int(image.size[1]/ratio)) # keep image aspect ratio
    image = image.resize(newsize, Image.ANTIALIAS)
    return image

class Pict_Frame(tix.Label):
    def __init__(self, parent=None, picture=None, maxupdate=None, **kwargs):
        tix.Label.__init__(self, parent, **kwargs)
        self.bind("<Configure>", self._resize_binding)
        self.maxupdate = maxupdate
        self.update_after_id = None
        self.photo = None
        self.image = None
        if picture:
            self.set_picture(picture)

    def _resize_binding(self, event):
        if self.photo:
            if not self.maxupdate:
                self.load_picture()
            else:
                if not self.update_after_id:
                    self.update_after_id = self.after(int(1000/self.maxupdate), self.load_picture)

    def load_picture(self):
        if self.photo:
            if self.update_after_id:
                self.update_after_id = None
            if (self.winfo_width() > 1) and (self.winfo_height() > 1): # prevent updates before widget gets sized
                self.image = ImageTk.PhotoImage(Resize_Image(self.photo, (
                             self.winfo_width()-int(self.cget("bd"))-1, self.winfo_height()-int(self.cget("bd"))-1))) 
                # here is where I added the constants ^^^
                # but even using cget to get the border size I have had to add to this
                # to prevent the resize loop, and when using other widget styles
                #(raised etc) this problem persists

                self.configure(image=self.image)

    def set_picture(self, filename):
        with open(filename, mode="rb") as file:
            self.photo = Image.open(file)
            self.photo.load() # load image into memory to allow resizing later without file access
        self.load_picture()

if __name__ == "__main__":
    test = Pict_Frame(bg="grey", bd=2, relief="raised",
                      maxupdate=2, # allows problem to be easily seen
                      picture="image.jpg")
    test.pack(fill="both", expand=True)
    test.master.mainloop()

当我应用其他样式时,例如较粗的边框 (10px),会出现这种调整大小的问题,这表明常量并不能真正解决问题.
那么有没有什么方法可以只获取小部件内部的空间,而不是其请求的大小?

when I apply other styles, such as a thicker border (10px) this resizing problem occurs showing that the constants don't really solve the problem.
so is there any method to get only the space inside the widget, instead of its requested size?

推荐答案

我相信我现在已经解决了这个问题,但它确实需要用不同的参数进行更多的测试以确保准确的结果.我用来测试的代码如下:

I believe I have now solved this, but it really needs a lot more testing with different parameters to ensure accurate results. The code I have use to test this is as follows:

from tkinter import tix
from PIL import Image, ImageTk

def Resize_Image(image, maxsize):
    r1 = image.size[0]/maxsize[0] # width ratio
    r2 = image.size[1]/maxsize[1] # height ratio
    ratio = max(r1, r2)
    newsize = (int(image.size[0]/ratio), int(image.size[1]/ratio)) # keep image aspect ratio
    image = image.resize(newsize, Image.ANTIALIAS)
    return image

class Pict_Frame(tix.Label):
    def __init__(self, parent=None, picture=None, maxupdate=None, imagesize=None, **kwargs):
        tix.Label.__init__(self, parent, **kwargs)
        self.bind("<Configure>", self._resize_binding)
        self.maxupdate = maxupdate
        self.imagesize = imagesize
        self.update_after_id = None # used for update rate limiting
        self.photo = None # used to store raw image from file for later use
        self.image = None # used for reference to the resized image
        if imagesize:
            self.photo=Image.new("RGB", (1,1)) # create empty image to insert
            self.image=ImageTk.PhotoImage(self.photo) # create instance of image for PIL
            self.configure(image=self.image)
            self.configure(width=imagesize[0], height=imagesize[1]) # not label uses pixels for size, set size passed in
        if picture:
            self.set_picture(picture) # we have a picture so load it now

    def _resize_binding(self, event):
        if self.photo: # we have a picture
            if not self.maxupdate: # no rate limiting
                self.load_picture()
            else:
                if not self.update_after_id: # if we're not waiting then queue resize
                    self.update_after_id = self.after(int(1000/self.maxupdate), self.load_picture)

    def load_picture(self):
        if self.photo:
            if self.update_after_id:
                self.update_after_id = None
            if (self.winfo_width() > 1) and (self.winfo_height() > 1): # prevent updates before widget gets sized
                bd = self.cget("bd") # get the border width
                if type(bd) != int: # if there was no border set we get an object back
                    pad = 4 # set this explicitly to avoid problems
                else:
                    pad = int(bd*2) # we have a border both sides, so double the retrieved value
                newsize = (self.winfo_width()-pad, self.winfo_height()-pad)
            elif self.imagesize: # only use the passed in image size if the widget has not rendered
                newsize = self.imagesize
            else:
                return # widget not rendered yet and no size explicitly set, so break until rendered
            self.image = ImageTk.PhotoImage(Resize_Image(self.photo, newsize))
            self.configure(image=self.image)

    def set_picture(self, filename):
        with open(filename, mode="rb") as file:
            self.photo = Image.open(file)
            self.photo.load() # load image into memory to allow resizing later without file access
        self.load_picture()

我的测试用例是:

import os
    path = "E:\imagefolder"
    images = []
    ind = 0
    for item in os.listdir(path): # get a fully qualified list of images
        if os.path.isdir(os.path.join(path, item)):
            if os.path.isfile(os.path.join(path, item, "thumb.jpg")):
                images.append(os.path.join(path, item, "thumb.jpg"))

    def callback():
        global ind
        ind += 1
        if ind >= len(images):
            ind = 0
        pict.set_picture(images[ind])

    ignore_test_cases = []

    if 1 not in ignore_test_cases:
        print("test case 1: - no border no set size")
        root = tix.Tk()
        tix.Button(root, text="Next Image", command=callback).pack()
        pict = Pict_Frame(parent=root, bg="grey",
                          maxupdate=2, # allows problem to be easily seen
                          picture=images[ind])
        pict.pack(fill="both", expand=True)
        tix.Button(root, text="Next Image", command=callback).pack()
        root.mainloop()

    if 2 not in ignore_test_cases:
        print("test case 2: - small border no set size")
        root = tix.Tk()
        tix.Button(root, text="Next Image", command=callback).pack()
        pict = Pict_Frame(parent=root, bg="grey", bd=2, relief="raised",
                          maxupdate=2,
                          picture=images[ind])
        pict.pack(fill="both", expand=True)
        tix.Button(root, text="Next Image", command=callback).pack()
        root.mainloop()

    if 3 not in ignore_test_cases:
        print("test case 3: - large border no set size")
        root = tix.Tk()
        tix.Button(root, text="Next Image", command=callback).pack()
        pict = Pict_Frame(parent=root, bg="grey", bd=10, relief="raised",
                          maxupdate=2,
                          picture=images[ind])
        pict.pack(fill="both", expand=True)
        tix.Button(root, text="Next Image", command=callback).pack()
        root.mainloop()

    if 4 not in ignore_test_cases:
        print("test case 4: - no border with set size")
        root = tix.Tk()
        tix.Button(root, text="Next Image", command=callback).pack()
        pict = Pict_Frame(parent=root, bg="grey",
                          maxupdate=2,
                          imagesize=(256,384),
                          picture=images[ind])
        pict.pack(fill="both", expand=True)
        tix.Button(root, text="Next Image", command=callback).pack()
        root.mainloop()

    if 5 not in ignore_test_cases:
        print("test case 5: - small border with set size")
        root = tix.Tk()
        tix.Button(root, text="Next Image", command=callback).pack()
        pict = Pict_Frame(parent=root, bg="grey", bd=2, relief="raised",
                          maxupdate=2,
                          imagesize=(256,384),
                          picture=images[ind])
        pict.pack(fill="both", expand=True)
        tix.Button(root, text="Next Image", command=callback).pack()
        root.mainloop()

    if 6 not in ignore_test_cases:
        print("test case 6: - large border with set size")
        root = tix.Tk()
        tix.Button(root, text="Next Image", command=callback).pack()
        pict = Pict_Frame(parent=root, bg="grey", bd=10, relief="raised",
                          maxupdate=2,
                          imagesize=(256,384),
                          picture=images[ind])
        pict.pack(fill="both", expand=True)
        tix.Button(root, text="Next Image", command=callback).pack()
        root.mainloop()

    if 10 not in ignore_test_cases:
        print("test case fullscreen: - small border no set size, in fullscreen window with expansion set up")
        root = tix.Tk()
        root.state("zoomed")
        root.grid_columnconfigure(1, weight=2)
        root.grid_columnconfigure(2, weight=1)
        root.grid_rowconfigure(2, weight=1)
        tix.Button(root, text="Next Image", command=callback).grid(column=2, row=1, sticky="nesw")
        pict = Pict_Frame(parent=root, bg="grey",# bd=10, relief="raised",
                          maxupdate=2,
                          picture=images[ind])
        pict.grid(column=2, row=2, sticky="nesw")
        tix.Button(root, text="Next Image", command=callback).grid(column=2, row=3, sticky="nesw")
        root.mainloop()

    if 11 not in ignore_test_cases:
        print("test case fullscreen: - small border no set size, in fullscreen window with expansion set up")
        root = tix.Tk()
        root.state("zoomed")
        root.grid_columnconfigure(1, weight=2)
        root.grid_columnconfigure(2, weight=1)
        root.grid_rowconfigure(1, weight=1)
        frame = tix.Frame(root)
        frame.grid(column=2, row=1, sticky="nesw")
        frame.grid_columnconfigure(1, weight=1)
        frame.grid_rowconfigure(2, weight=1)
        tix.Button(frame, text="Next Image", command=callback).grid(column=1, row=1, sticky="nesw")
        pict = Pict_Frame(parent=frame, bg="grey",# bd=10, relief="raised",
                          maxupdate=2,
                          picture=images[ind])
        pict.grid(column=1, row=2, sticky="nesw")
        tix.Button(frame, text="Next Image", command=callback).grid(column=1, row=3, sticky="nesw")
        root.mainloop()

我对这段代码遇到的唯一问题是,当我在全屏应用程序中使用小部件时,在使用网格方法并设置右列的权重时,重新调整大小无法按预期工作到 1(使用 pict 小部件)和左列(空)到 1,右列最终占据屏幕宽度的大约 2/3.

The only issue I have had with this code is that when I am using the widget in a full-screen application the re-sizing doesn't work as intended, when using the grid method and setting the weight of the right column to 1 (with the pict widget) and the left column (empty) to 1, the right column ends up taking approx 2/3rds the width of the screen.

我怀疑这是由于显式设置图像的大小,然后使其变宽,这意味着几何管理器希望使其更宽(无限大),直到达到某种平衡.但是,如果有人能对此(甚至解决方案)有所了解,我们将不胜感激.

I suspect this is due to explicitly setting the size of the image, which then makes it wider, meaning the geometry manager wants to make it wider still (ad infinitum) until it reaches some equilibrium. But if anyone can shed any light on this (or even a solution) it would be appreciated.

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

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