如何使tkinter画布背景透明?

11

我正在制作一个国际象棋程序,希望能够拖动棋子。为了实现这个功能,我将棋子的图像放在一个Canvas上,以便可以拖动它(如果需要也可以使用Label)。但是,当我拖动棋子时,会出现一个白色的方框围绕着棋子的图像。

输入图像描述

当我调查这个问题时,许多人给出了以下解决方案:

drag_canvas = Canvas(self, height=80, width=80, bg="yellow")
root.wm_attributes("-transparentcolor", "yellow")

这导致背景透明,但可见的不是棋盘,而是GUI后面的程序。

enter image description here.

是否有任何方法可以使背景透明并显示在tkinter窗口后面的棋盘,而不是程序?

注意:我不介意使用任何其他小部件(例如Label),但它们必须使用Python默认附带的模块(因此不能使用PIL),因为该程序需要在无法下载其他模块的环境中使用。


阅读此使用图像更新Tkinter标签的内容。 - stovfl
@stovfl 那使用的是 PIL。 - Anonymous Coder
你的图片已经是透明的了,为什么要使用.Canvas(...?[编辑]你的问题并添加有关图像格式的信息。 - stovfl
@stovfl 是的,它们是透明的,但为了能够在棋盘上拖动它们,我需要将它们放在某种小部件上。这个小部件有一个不透明的背景,所以我需要使背景透明。我只是使用了一个 Canvas,因为根据stackoverflow上类似的帖子,我认为这是最好的方法。因此,如果有一种不使用 Canvas 就可以实现这一点的方法,我会非常乐意使用它。 - Anonymous Coder
@匿名程序员。我在哪里可以找到黑白图片? - undefined
4个回答

7

问题:如何使tkinter画布的背景透明?

唯一可能的config(...选项是将背景设置为空。

c.config(bg='')

results with: _tkinter.TclError: unknown color name ""


要得到这个结果:

enter image description here

你需要将棋盘和棋子放在同一个.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)

已使用Python: 3.5 - Tk版本:8.6进行测试


6
抱歉,问题是如何获得一个透明画布。您提供了一个解决方法,建议将图像放在同一个画布上。这怎么能回答问题呢?在我的情况下,我需要将我的图像和背景放在不同的画布上。 - 10 Rep
提问者说:“我不介意使用任何其他小部件”,tk PhotoImage对于棋盘上的棋子来说足够接近。可以使用Pillow获取透明背景。 - Terry Jan Reedy

5
一个仅适用于 Windows 的解决方案是使用 pywin32 模块,可以通过以下方式安装:

pip install pywin32

使用 pywin32,您可以更改窗口 exstyle 并将画布设置为分层窗口。 分层窗口可以具有透明的颜色键,并在下面的示例中完成:
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()

说明:

首先,我们需要窗口的句柄,它被称为hwnd,我们可以通过.winfo_id()在tkinter中获取它。

接下来,我们通过GetWindowLong获取实际的扩展窗口样式,并使用win32con.GWL_EXSTYLE来获取特定的扩展样式信息。

之后,我们进行十六进制的按位操作,使用wnd_exstyle | win32con.WS_EX_LAYERED来修改样式,结果就是我们的new_style

现在我们可以使用SetWindowLong为窗口设置扩展样式。最后,我们有了一个分层窗口,它具有额外的属性,我们可以进行操作。我们可以使用SetLayeredWindowAttributes设置透明的ColorKey,虽然我们只使用LWA_COLORKEY,但alpha参数对我们没有用处。 重要提示:在定义透明的colorkey之后,该画布中所有带有该颜色的内容都将变为透明。

enter image description here


1

来自@thingamabobs的仅适用于Windows的解决方案也可以通过Python自带的windll进行操作(在Surface Pro 8上是3.11版本,Win11系统)。

我将其转换为一个函数:

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

对于您来说,如果可拖动的画布变为透明,它的白色背景将消失。也就是说,当棋子是一个填充了白色(或黑色...)形状的画布,并且背景为colorkey时,背景将消失... 正如下面的两个画布所示。
只是为了完整起见,我注意到Canvas cvs_upper的任何子元素中具有透明颜色colorkey的部分,即使画布本身可能有绘图并且不透明(例如下面图片中的红色条),也会变为透明。
这通过第二个画布上的一个按钮来说明,其文本显示为colorkey颜色。
在窗口变为透明之前,我们看到的是这样的: before the upper canvas is transparent 画布很暗(#010203非常暗!),按钮文本与相同颜色。一秒钟后,上层画布变为透明。

After the upper canvas is made transparent

您可以看到画布和按钮文本现在是透明的,我们可以看到它下面的第一个画布。我们看不到按钮下面的红色条!

注意:要将透明画布恢复为不透明状态,请使用'windll.user32.SetWindowLongA(hwnd, -20, 4)' #GWL_EXSTYLE =4来设置。 - undefined

0

使用Tkinter是不可能的。


5
这个答案可以通过提供更多的事实和参考来改进。此外,我删除了你对这个平台的诋毁。我们不欣赏你的缺乏礼貌。如果你不喜欢看到的东西,请成为改变的一部分。 - starball

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接