首页 > 解决方案 > 在 Tk.Canvas 中制作新的 Pillow 图像时,为什么数组有效但变量无效?

问题描述

我正在尝试在 Tkinter 中创建一个半透明(RGBA)矩形。我使用这个答案中的代码片段来制作它,但我无法理解数组在这里的作用。他们最初想要制作多个矩形,所以我理解他们为什么会在这方面使用它,但是当我尝试将数组更改为一个变量时,据我所知,它会做同样的事情,它会停止显示矩形颜色。

这是带有输出数组的(稍作修改)代码:

from tkinter import *
from PIL import Image, ImageTk

root = Tk()
images = []  # to hold the newly created image

def create_rectangle(x1, y1, x2, y2, **kwargs):  # x & y's dimensions
    alpha = int(kwargs.pop('alpha') * 255)
    fill = kwargs.pop('fill')
    fill = root.winfo_rgb(fill) + (alpha,)  # Returns a tuple of the color
    image = Image.new('RGBA', (x2-x1, y2-y1), fill)
    images.append(ImageTk.PhotoImage(image))
    canvas.create_image(x1, y1, image=images[-1], anchor='nw')
    canvas.create_rectangle(x1, y1, x2, y2, **kwargs)

canvas = Canvas(width=300, height=300)
canvas.pack()

create_rectangle(50, 50, 250, 150, fill='green', alpha=.5)

root.mainloop()

用它的输出:

在此处输入图像描述

以及带有变量而不是images[-1]after的相同代码image=

def create_rectangle(x1, y1, x2, y2, **kwargs):  # x & y's dimensions
    alpha = int(kwargs.pop('alpha') * 255)
    fill = kwargs.pop('fill')
    fill = root.winfo_rgb(fill) + (alpha,)  # Returns a tuple of the color
    image = Image.new('RGBA', (x2-x1, y2-y1), fill)
    test_var = ImageTk.PhotoImage(image)
    canvas.create_image(x1, y1, image=test_var, anchor='nw')
    canvas.create_rectangle(x1, y1, x2, y2, **kwargs)

哪个输出:

在此处输入图像描述

如果我打印type()两者,我会得到相同的结果(<class 'PIL.ImageTk.PhotoImage'>),并且定义相同的变量并附加它(images.append(test_var))会给我正确的结果。只有在使用变量时canvas.create_image才会出现错误。

有谁知道发生了什么?

标签: pythontkintercanvaspython-imaging-librarytkinter-canvas

解决方案


存在错误PhotoImage-Garbage CollectorPhotoImage其分配给局部变量时从内存中删除,然后您会看到空图像。

您必须将其分配给global变量

def create_rectangle(...):
    global test_var

    # ... code ...

    test_var = ...

    # ... code ...

或者您可以将其分配给其他对象。
将它分配给一些将显示它的小部件是很流行的(Canvas, Label, Button)。

def create_rectangle(...):

    # ... code ...

    test_var = ...
    canvas.test_var = test_var

    # ... code ...

请参阅PhotoImageNote的文档


import tkinter as tk  # PEP8: `import *` is not preferred
from PIL import Image, ImageTk

# --- classes ---

# ... empty ...

# --- functions ---

def create_rectangle(x1, y1, x2, y2, **kwargs):  # x & y's dimensions
    #global photo
    
    alpha = int(kwargs.pop('alpha') * 255)
    fill = kwargs.pop('fill')
    fill = root.winfo_rgb(fill) + (alpha,)  # Returns a tuple of the color
    
    image = Image.new('RGBA', (x2-x1, y2-y1), fill)
    photo = ImageTk.PhotoImage(image)
    #images.append(photo)
    canvas.photo = photo
    
    canvas.create_image(x1, y1, image=photo, anchor='nw')
    canvas.create_rectangle(x1, y1, x2, y2, **kwargs)

# --- main ---

#images = []  # to hold the newly created image

root = tk.Tk()

canvas = tk.Canvas(width=300, height=300)
canvas.pack()

create_rectangle(50, 50, 250, 150, fill='green', alpha=.5)

root.mainloop()

PEP 8——Python 代码风格指南


编辑:

正如@acw1668 在评论中提到的那样:如果您将运行create_rectangle多次,那么它会将新图像分配给global变量,或者canvas.photo将从Garbage Collector内存中删除以前的图像并且图像将消失。因此,如果您需要显示所有图像,请将它们全部保留在列表中。


推荐阅读