【问题标题】:When making a new Pillow image in Tk.Canvas, why does an array work but not a variable?在 Tk.Canvas 中制作新的 Pillow 图像时,为什么数组有效但变量无效?
【发布时间】:2026-02-02 17:45:01
【问题描述】:

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

这是(稍作修改的)代码,其中包含用于输出的数组:

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

用它的输出:

同样的代码在image=之后有一个变量而不是images[-1]

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中使用变量时才会出现bug。

有人知道发生了什么吗?

【问题讨论】:

    标签: python tkinter canvas python-imaging-library tkinter-canvas


    【解决方案1】:

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

    您必须将其分配给global 变量

    def create_rectangle(...):
        global test_var
    
        # ... code ...
    
        test_var = ...
    
        # ... code ...
    

    或者您可以将其分配给其他对象。
    将其分配给将显示它的某些小部件(CanvasLabelButton)很流行。

    def create_rectangle(...):
    
        # ... code ...
    
        test_var = ...
        canvas.test_var = test_var
    
        # ... code ...
    

    请参阅 PhotoImage 的文档中的 Note


    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 -- Style Guide for Python Code


    编辑:

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

    【讨论】:

    • 如果您多次调用create_rectangle(),之前的图像仍会被垃圾回收。这就是为什么使用列表来存储链接解决方案中的图像引用。
    • @acw1668 是的,我正在考虑这个问题。我添加了你的建议来回答。
    最近更新 更多