问题描述
我目前正在尝试在画布上显示图像。更具体地说,我想让画布上绘制的图像根据窗口的大小调整大小(这样图像总是适合画布)。
我从一个填满整个屏幕的简单画布开始。
import tkinter as tk
from PIL import Image,ImageTk
root = tk.Tk()
WIDTH,HEIGHT = root.winfo_screenwidth(),root.winfo_screenheight()
root.geometry('%dx%d+0+0' % (WIDTH,HEIGHT))
canvas = tk.Canvas(root)
canvas.pack(fill="both",expand=True)
然后我会设置我的背景图像和能够在背景上调整大小的图像。
backgroundImage = ImageTk.PhotoImage(Image.open("filepath"))
image1 = Image.open("filepath")
image2 = ...
....
....
然后我创建了一种调整图像大小的方法。
"""
This method resizes a image so that all the images fits on the GUI. This first creates an Image object,but since the Image object does not allow access to the width and height of the Image object,a ImageTk
object needs to be created from the Image object. The ImageTk object cannot resize,but the Image object
can. So using ImageTk object to get the height and width and the Image object to resize,a new Image object
that is resized to fit the GUI is created.
@imageFile- the image file that is being resized
@windowMeasure- the measurement of the window to proportionally resize the image
@useHeight- determine the measurement being proportioned
"""
def resizedImageTk(image,windowMeasure,useHeight):
imageTk = ImageTk.PhotoImage(image)
area = windowMeasure * 4/5
tileSize = area / 4
if useHeight:
proportion = tileSize / imageTk.height()
else:
proportion = tileSize / imageTk.width()
resizedImage = image.resize((int(imageTk.width()*proportion),int(imageTk.height()*proportion)))
resizedImageTk = ImageTk.PhotoImage(resizedImage)
return resizedImageTk
然后我使用一种方法在窗口大小发生变化时重新绘制调整大小的图像并将其绑定到根。注意:我知道这可能需要大量计算,因此我减少了这种情况发生的次数
numResizes = 0
def handle_configure(event):
if numResizes % 5 == 0:
geometry = root.geometry()
width = int(geometry[0:geometry.index("x")])
height = int(geometry[geometry.index("x")+1:geometry.index("+")])
canvas.create_image((0,0),image=backgroundImageTk,anchor="nw")
if height < width:
measurement = height
else:
measurement = width
resizedImage1 = resizedImageTk(image1,measurement,height < width)
resizedImage2 = ....
....
....
images = [resizedImage1,resizedImage2,...]
imageWidth = resizedImage1.width()
imageHeight = resizedImage1.height()
i = 0
for row in range(0,int(len(images) / 4)):
for column in range(0,int(len(images) / 5):
x = imageWidth*column + int(width/2) - imageWidth * 2
y = imageHeight*row + int(height/2) - int(imageHeight*2.5)
canvas.create_image((x,y),image=images[i])
i=i+1
numResizes = numResizes + 1
root.bind("<Configure>",handle_configure)
root.mainloop()
我已经用我的图像运行了这个并取得了一些成功,但是,它不能完全工作。我有我的背景图片显示,但我的其他图片没有。我不知道为什么,因为当我在嵌套 for 循环(未显示图像的位置)中对画布使用 create_line 函数时,我确实显示了线条。
如果有人能就此提供一些建议,我将不胜感激!
谢谢
更新:
这是我正在尝试做的一个简单示例。您可以使用任何示例图片来对此进行测试。
import tkinter as tk
from PIL import Image,HEIGHT = int(root.winfo_screenwidth() * 103/104),int(root.winfo_screenheight() * 11/12)
root.geometry('%dx%d+0+0' % (WIDTH,expand=True)
testimage = Image.open("enter file path here!")
testimageTk = ImageTk.PhotoImage(testimage)
resizedTestimage = None
resizedTestimageTk = None
def handle_configure(event):
geometry = root.geometry()
width = int(geometry[0:geometry.index("x")])
height = int(geometry[geometry.index("x")+1:geometry.index("+")])
useHeight = height < width
if useHeight:
measurement = height
else:
measurement = width
if useHeight:
proportion = measurement / testimageTk.height()
else:
proportion = measurement / testimageTk.width()
resizedTestimage = testimage.resize((int(testimageTk.width()*proportion),int(testimageTk.height()*proportion)))
resizedTestimageTk = ImageTk.PhotoImage(resizedTestimage)
canvas.create_image((0,image=resizedTestimageTk,anchor="nw")
print("(image width,image height): (" + str(resizedTestimageTk.width()) + " " + str(resizedTestimageTk.height()) + ")")
root.bind("<Configure>",handle_configure)
root.mainloop()
解决方法
您的新代码在 PhotoImage
中存在错误。
在 handle_configure
中,即使您已经创建了外部/全局变量 resizedTestImageTk
如果我通知函数它必须使用 resizedTestImageTk
来通知函数它必须将它分配给外部变量而不是本地变量,这对我有用
global
或者我将本地 def handle_configure(event):
global resizedTestImageTk # <-- inform function to assign image to global variable instead of using local one
分配给全局类。
resizedTestImageTk
最少的工作代码。
我更改了一些计算以使其更具可读性。
canvas.resizedTestImageTk = resizedTestImageTk # <-- assign local `resizedTestImageTk` to global class.
和测试图像
维基百科:Lenna
编辑:
我看到了其他问题 - 不太明显。您总是放置新图像但不会删除旧图像 - 所以最终它可能会使用更多内存。
您可以在开始时将图像放在画布上 - 并获取其 ID
import tkinter as tk
from PIL import Image,ImageTk
# --- functions ---
def handle_configure(event):
global resizedTestImageTk # <-- inform function to assign image to global variable instead of using local one
geometry = root.geometry()
window_width = int(geometry[0:geometry.index("x")])
window_height = int(geometry[geometry.index("x")+1:geometry.index("+")])
image_width = testImageTk.height()
image_height = testImageTk.width()
if window_height < window_width:
proportion = window_height / image_height
else:
proportion = window_width / image_width
image_width = int(image_width * proportion)
image_height = int(image_height * proportion)
resizedTestImage = testImage.resize((image_width,image_height))
resizedTestImageTk = ImageTk.PhotoImage(resizedTestImage)
canvas.create_image((0,0),image=resizedTestImageTk,anchor="nw")
#canvas.resizedTestImageTk = resizedTestImageTk # <-- assign local `resizedTestImageTk` to global class.
print(f"(image width,image height): ({image_width} {image_height})")
# --- main ---
root = tk.Tk()
WIDTH = int(root.winfo_screenwidth() * 103/104)
HEIGHT = int(root.winfo_screenheight() * 11/12)
root.geometry('%dx%d+0+0' % (WIDTH,HEIGHT))
canvas = tk.Canvas(root)
canvas.pack(fill="both",expand=True)
testImage = Image.open("lenna.png")
testImageTk = ImageTk.PhotoImage(testImage)
resizedTestImage = None
resizedTestImageTk = None
root.bind("<Configure>",handle_configure)
root.mainloop()
然后使用 ID 替换图像
image_id = canvas.create_image((0,image=testImageTk,anchor="nw")
最小工作代码:
canvas.itemconfig(image_id,image=resizedTestImageTk)