20

I need to make my tkinter rectangles transparent. Does anyone know how to do that?

I have tried to specify alpha=".5", opacity=".5", and I have tried to add two more digits in the end of the color code: fill="#ff000066". None of these things seem to work, I can't find the right syntax.

import tkinter

root = tkinter.Tk()
canvas = tkinter.Canvas(root, width=800, height=600)
canvas.pack()

canvas.create_rectangle(50, 50, 100, 100, fill="#ff0000", alpha=0.5)

root.mainloop()

This code gives me this message: _tkinter.TclError: unknown option "-alpha", so this is obviously not the right way to do it.

martineau
  • 119,623
  • 25
  • 170
  • 301
Jånny
  • 205
  • 1
  • 2
  • 7
  • Forgive me if I'm wrong, but IIRC tkinter supports four color formats: `#rgb`, `#rgba`, `#rrggbb`, and `#rrggbbaa`. What happens when you try `fill="#ff000080"`? (I'm pretty sure this is valid for HTML/CSS, not sure for tk). – Green Cloak Guy Feb 11 '19 at 19:35
  • It doesn't seem like it supports #rrggbbaa. Trying what you said gives me this error: _tkinter.TclError: invalid color name "#ff000080" – Jånny Feb 11 '19 at 19:39
  • 1
    It's not a syntax issue. `tkinter` doesn't support the transparency of items on a `Canvas`. There are ways to make an entire top-level window transparent, but there's nothing to control it for individual widgets below that level (including `Canvas` objects). – martineau Feb 11 '19 at 19:48
  • 2
    Try adding `stipple='gray50'`, but it is not true transparency. – acw1668 Feb 11 '19 at 23:26

2 Answers2

33

You can use transparent image to simulate the result. Use Pillow to create transparent image and then use canvas.create_image(...) to draw it. Below is a sample code:

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):
    if 'alpha' in kwargs:
        alpha = int(kwargs.pop('alpha') * 255)
        fill = kwargs.pop('fill')
        fill = root.winfo_rgb(fill) + (alpha,)
        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=200)
canvas.pack()

create_rectangle(10, 10, 200, 100, fill='blue')
create_rectangle(50, 50, 250, 150, fill='green', alpha=.5)
create_rectangle(80, 80, 150, 120, fill='#800000', alpha=.8)

root.mainloop()

And the output:

enter image description here

acw1668
  • 40,144
  • 5
  • 22
  • 34
  • 8
    You need to install the `Pillow` module. – acw1668 Dec 01 '19 at 23:33
  • @acw1668 no matter what I try in my application image is always white. Did you face smt like this before? – Alper91 Jan 17 '21 at 14:13
  • @Alper91 Did you save a reference of the instance of `ImageTk.PhotoImage()`? – acw1668 Jan 17 '21 at 15:07
  • 1
    @acw1668 yeap, it turns out the problem is with 'Image.new('RGBA', (x2-x1, y2-y1), fill)' Iencode colors in Hex: #0101FF -> it should decode it as 1,1,255 but it devils: 257, 65332, 21122. I decoded myself: (int(color[1:3],16),int(color[3:5],16),int(color[5:7],16),55) now it works. Thanks though. – Alper91 Jan 17 '21 at 15:10
  • @Alper91 can you explain how to decode this, I'm stuck with the same problem – Thalis Jul 07 '21 at 08:32
  • 1
    `winfo_rgb()` returns color component values in the range of 0-65535, but RGB color components s in Pillow are 8-bit with a range of 0-255. So this code is passing fill colors of `(0, 32896, 0, 127)` and `(32896, 0, 0, 204)`. It seems to work, so is pillow doing an (undocumented) `% 256` on the values it receives? – martineau Jan 02 '22 at 11:19
  • `winfo_rgb("#abcdef") => (0xabab, 0xcdcd, 0xefef)` so we're lucky that the color information is in both the high and low bytes. if that weren't the case, even any reduction to 8 bit done by PIL would have caused useless values. – Christoph Rackwitz Jan 13 '22 at 11:39
  • is there a way to do with lines – Dodu May 23 '22 at 07:22
11

In a canvas if you want some widget to be transparent, simply let the fill parameter be empty:

fill=""
Das_Geek
  • 2,775
  • 7
  • 20
  • 26
delonglée
  • 119
  • 1
  • 2