1

Is there any way to set an image as partially transparent, maybe using PIL or something? I know tkinter has a feature like this for Tk() and Toplevel(), but I wanted to know if there is a way to apply it to an a widget or maybe a PIL image that I can then put in a widget, either would do.

I want to make a game that fades to black when you lose, but I don't want the whole window to fade away.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • 4
    Definitely not - Tkinter widgets are inherently opaque. You may be able to make an image on a Canvas transparent (it's not something I've ever tried), but it would only be transparent with respect to other drawing objects on the same Canvas. – jasonharper Jun 23 '20 at 15:09
  • Relevant [make that particular widget transparent in color.](https://stackoverflow.com/a/62494468/7414759) – stovfl Jun 23 '20 at 20:13

1 Answers1

1

You have two options: fade out the whole window or fade out an image using PIL, individual widgets cannot be faded out:

Fading out a window

Tk and TopLevel windows can be faded out entirely

import time
import threading

import tkinter

root = tkinter.Tk()

def fade():
    global root
    # Walk backwards through opacities (1 is opaque, 0 is transparent)
    i = 1.0
    while i >= 0:
        root.attributes("-alpha", i)
        i -= 0.1
        # Sleep some time to make the transition not immediate
        time.sleep(0.05)
    
# Put image fading in a thread so it doesn't block our GUI
fade_thread = threading.Thread(target=fade)
tkinter.Button(root, text="Fade out", command=fade_thread.start).pack()

root.mainloop()

Fading out an image

A bit more involved and a bit more computationally intensive (larger images exacerbate this problem). It could be worth precomputing these or using less steps (-10 vs -5 etc) to save some compute power.

import time
import threading

import tkinter
from PIL import Image, ImageTk

root = tkinter.Tk()

# Tested with .jpg and .png
IMAGE_PATH = "/path/to/image.jpg"

# Create a pillow image and a tkinter image. convert to RGBA to add alpha channel to image
image = Image.open(IMAGE_PATH).convert("RGBA")
image_tk = ImageTk.PhotoImage(image)

# We'll fade to whatever the background is here (black, white, orange, etc)
label = tkinter.Label(root, image=image_tk, bg="black")
label.pack()

def fade_image():
    global image, image_tk, label
    # Walk backwards through opacities (255 is opaque, 0 is transparent)
    for i in range(255, 0, -5):
        image.putalpha(i) # Set new alpha
        image_tk = ImageTk.PhotoImage(image) # Cretae new image_tk
        label.configure(image=image_tk)
        # Sleep some time to make the transition not immediate
        time.sleep(0.001)
    
# Put image fading in a thread so it doesn't block our GUI
fade_thread = threading.Thread(target=fade_image)
tkinter.Button(root, text="Fade To Black", command=fade_thread.start).pack()

root.mainloop()

Note this is bad practice in regards to using threads and time.sleep() inside a GUI program. Using widget.after(delay_in_ms, callback) is preferable. For more on how to do that check out tkinter: how to use after method

Tresdon
  • 1,211
  • 8
  • 18
  • Nice answer but can be improved. You can use [`after`](https://stackoverflow.com/questions/25753632/tkinter-how-to-use-after-method) method instead of threading and also, remove `.resize((600,400))` as it was flickering when I ran your code with a smaller image. – Saad Jun 23 '20 at 16:17
  • Good point, removed the resize and added a note about using .after instead – Tresdon Jun 23 '20 at 16:24