0

I'm trying to create a translucent (RGBA) rectangle in Tkinter. I used the code snippet from this answer to make it, but I'm having trouble understanding what the array does here. They initially wanted to make multiple rectangles, so I understand why they would use it in that respect, but when I try to change the array to a variable that, in my understanding, would do the same, it stops showing the rectangle color.

Here is the (slightly modified) code with an array for the output:

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

With its output:

enter image description here

And the same code with a variable instead of 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)

Which outputs:

enter image description here

If I print the type() of both, I get the same result (<class 'PIL.ImageTk.PhotoImage'>), and defining the same variable and appending it (images.append(test_var)) gives me the correct result. It's only when the variable is used in canvas.create_image that the bug appears.

Does anyone know what is happening?

Cortisol
  • 15
  • 5

1 Answers1

1

There is bug in PhotoImage - Garbage Collector removes PhotoImage from memory when it is assigned to local variable and then you see empty image.

You have to assign it to global variable

def create_rectangle(...):
    global test_var

    # ... code ...

    test_var = ...

    # ... code ...

or you can assign it to other object.
It is popular to assing it to some widget which will display it (Canvas, Label, Button).

def create_rectangle(...):

    # ... code ...

    test_var = ...
    canvas.test_var = test_var

    # ... code ...

See Note in documentation for PhotoImage


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


EDIT:

As @acw1668 mentioned in comment: if you will run create_rectangle many times then it will assign new image to global variable or canvas.photo and Garbage Collector will remove previous image from memory and image will disappear. So if you need to display all images then keep them all on list.

furas
  • 134,197
  • 12
  • 106
  • 148
  • 1
    If you call `create_rectangle()` more than once, the previous image will still be garbage collected. That is why list is used to store the image references in the linked solution. – acw1668 Apr 14 '21 at 01:33
  • @acw1668 yes, I was thinking about this. I added your suggestion to answer. – furas Apr 14 '21 at 01:40