Reputation: 542
I am making a chess program and I want to be able to drag the pieces. In order to do this, I put the image of the piece on a Canvas
so it can be dragged (I can also use a Label
if I want). However, when I drag the piece there is a white square that surrounds the image of the piece.
When I researched the problem, many people gave this solution:
drag_canvas = Canvas(self, height=80, width=80, bg="yellow")
root.wm_attributes("-transparentcolor", "yellow")
This caused the background to be transparent but it was not the chessboard that was visible, it was the program behind the GUI
Is there any way I can have the background be transparent and show the chessboard behind rather than the program behind the tkinter window?
Note: I do not mind using any other widget (e.g. a Label
) but they must use modules that come default with Python (so no PIL) as this program needs to be used in an environment where I cannot download other modules.
Upvotes: 11
Views: 41108
Reputation: 114
Assume you have a parent object for your drag_canvas
called chess_table
, no matter it is a Canvas
or Toplevel
, just do this:
drag_canvas.config(bg=chess_table["bg"])
Done... That's it.
Upvotes: 0
Reputation: 8072
A windows only solution is to use the pywin32
module that can be installed with:
pip install pywin32
With pywin32
you can alter the window exstyle and set the canvas to a layered window. A layered window can have a transparent colorkey and is done in the example below:
import tkinter as tk
import win32gui
import win32con
import win32api
root = tk.Tk()
root.configure(bg='yellow')
canvas = tk.Canvas(root,bg='#000000')#full black
hwnd = canvas.winfo_id()
colorkey = win32api.RGB(0,0,0) #full black in COLORREF structure
wnd_exstyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
new_exstyle = wnd_exstyle | win32con.WS_EX_LAYERED
win32gui.SetWindowLong(hwnd,win32con.GWL_EXSTYLE,new_exstyle)
win32gui.SetLayeredWindowAttributes(hwnd,colorkey,255,win32con.LWA_COLORKEY)
canvas.create_rectangle(50,50,100,100,fill='blue')
canvas.pack()
First we need the handle of the window which is called hwnd and we can get it in tkinter by .winfo_id()
.
Next we get the actual extended window style by GetWindowLong and ask specific for extended style information with win32con.GWL_EXSTYLE
.
After that we do a bitwise operation in hexadecimal to alter the style with wnd_exstyle | win32con.WS_EX_LAYERED
the result is our new_style
.
Now we can set the extended style to the window with SetWindowLong. Finally we have our LayeredWindow which has additional Attributes we can work with. A transparent ColorKey can be set with SetLayeredWindowAttributes
while we just use LWA_COLORKEY
the alpha parameter has no use to us.
Important note: After defining a transparent colorkey, everything in that canvas with that color will be transparent.
Upvotes: 6
Reputation:
I mean to see that you have provided an answer to your own question already in your question itself, but it appears to me at the same time also that you are not aware of providing a solution to the experienced issue because you are focused on another way of solving the by you faced problem.
Sometimes the most obvious facts stay undetected buried deep below all of the other thoughts while searching for a solution to an experienced issue.
If what you describe in your question works as described (haven't tested it yet), the most straightforward solution which details you have already described in your question is to use two windows one above the other. The window in the background displays the same chessboard as the window above it.
Now if you move your piece having transparent background you will see through the transparent part of the piece the chessboard displayed by the window in background creating this way the illusion of moving only the visible part of the chess-piece over the chessboard displayed in the foreground window.
I would be glad if you could report if this worked out for you. It's a work-around, but should give you the expected behavior.
Now if you move your piece having transparent background you will see around the piece the chessboard displayed by the window in background creating the illusion that the image transparency lets you view the canvas background you move the piece around on.
Upvotes: 0
Reputation: 667
The windows only solution from @thingamabobs works also via windll
which comes standard with python (3.11 on surface pro 8, win11),
I converted it into a function:
import tkinter as tk
from ctypes import windll
def maketransparent(w):
# the translated windll part...
# a COLORREF structure is a reverse RGB order int!
# see https://www.pinvoke.net/search.aspx?search=COLORREF&namespace=[All]
# here we chose nearly black so real black (#000000) still shows up
colorkey = 0x00030201
hwnd = w.winfo_id()
wnd_exstyle = windll.user32.GetWindowLongA(hwnd, -20) # GWL_EXSTYLE
new_exstyle = wnd_exstyle | 0x00080000 # WS_EX_LAYERED
windll.user32.SetWindowLongA(hwnd, -20, new_exstyle) # GWL_EXSTYLE
windll.user32.SetLayeredWindowAttributes(hwnd, colorkey, 255, 0x00000001) # LWA_COLORKEY = 0x00000001
win=tk.Tk()
win.geometry('400x200')
win.grid_rowconfigure(0,weight=1)
win.grid_columnconfigure(0,weight=1)
cvs_lower=tk.Canvas(win, background='lightgreen')
cvs_lower.create_rectangle(50, 50, 350, 150, fill='blue')
cvs_lower.grid(row=0, column=0, sticky='nesw')
cvs_upper=tk.Canvas(win, background='#010203') #dont use all black, screws up any black trext on your canvas...
cvs_upper.create_rectangle(325, 25, 375, 175, fill='red')
cvs_upper.grid(row=0, column=0, sticky='nesw')
btn=tk.Button(cvs_upper, text='plop', foreground='#010203', font='Arial 30')
btn.pack(side='right')
win.after(2000, lambda:maketransparent(cvs_upper))
win.mainloop()
So for you, if the draggable canvas is made transparent, its white background will disapear. As in, When chess piece is a white (or black..) filled shape on a canvas with background=colorkey
that back ground will disappear... as is demonstrated with the two canvasses here
Just for completeness, I noticed that any portion of any child of the Canvas cvs_upper
that has the transparency color colorkey
will also be transparent even if the canvas itself may have a drawing on it and is not transparent (e.g. the red bar in the images below).
this is illustrated with a button on the second canvas with its text displayed in the colorkey
color.
before the windows is made transparent we see this:
The canvas is dark (#010203 is v dark!) and the button text is the same color. After a second the upper canvas becomes transparent:
as you can see the canvas and the button text are now transparent, and we see the first canvas below it. We do not see the red bar below the button!
Upvotes: 1
Reputation: 15533
Question: How to make a tkinter canvas background transparent?
The only possible config(...
option, to set the background to nothing
c.config(bg='')
results with: _tkinter.TclError: unknown color name ""
To get this result:
you have to hold the chess board and figures within the same .Canvas(...
.
self.canvas = Canvas(self, width=500, height=200, bd=0, highlightthickness=0)
self.canvas.create_rectangle(245,50,345,150, fill='white')
self.image = tk.PhotoImage(file='chess.png')
self.image_id = self.canvas.create_image(50,50, image=self.image)
self.canvas.move(self.image_id, 245, 100)
Tested with Python: 3.5 - TkVersion: 8.6
Upvotes: 6