OS: Windows 10
Python 3.8
I am trying to display PNG images with transparency with no titlebar, or any other indication of window info (imagine sticking a sticker on your screen). I've already done this once in another language by putting an image in a window and removing all the elements of the window, including titlebar and background, but I cannot achieve it in python.
I have already tried Tkinter, but seems like it's not designed to work with PNG transparency and it cannot have separate transparency for parent and child windows.
Now I am trying wxPython. My idea is that it might be possible to make the parent window transparent and the child window (which holds the image) opaque, but cannot figure out if that's even possible.
Here is the simplified version of my attempt:
app = wx.App()
path = 'Images/02.png'
bitmap = wx.Bitmap(path, wx.BITMAP_TYPE_PNG) # Create bitmap
frame = wx.Frame(None,-1,'Transparent Window',size=(1000,1000)) # Create frame (top level window)
frame.SetTransparent(100) # Change transparency of the frame
panel = wx.Panel(frame, -1) # Create panel (to hold the image)
wx.StaticBitmap(panel, -1, bitmap) # Put the image in the panel
panel.SetTransparent(255) # Change transparency of the panel (the image)
frame.Show()
app.MainLoop()
But unfortunately ".SetTransparent()" only works on top level window, so I can't set the transparency of the panel to anything else.
But basically, the question is, is it possible to display image with transparency on its own with Python?
I am not looking for any specific method of achieving this, the one I provided was just the only one I know. So please help me if you can :)
Edit 1:
Tried to use wx.lib.agw.advancedsplash:
import wx
import wx.lib.agw.advancedsplash as AS
app = wx.App()
frame = wx.Frame(None, -1, "AdvancedSplash Test")
imagePath = "Images/05.png"
bitmap = wx.Bitmap(imagePath)
splash = AS.AdvancedSplash(frame, bitmap=bitmap)
app.MainLoop()
But it still fills in transparent areas
You may want to look into the implementation of AdvancedSplash in wx.lib.agw.advancedsplash. It’s sort of a splash screen that adapts itself to the picture you use, and if you have transparencies they will be taken into account.
Transparency is be applied to the frame, so don't worry about the panel, the whole thing becomes transparent.
Be careful how you run this code, as it needs to be killed or Ctrl-C'd.
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, pos=(200,200), style=wx.FRAME_NO_TASKBAR)
panel1 = wx.Panel(self)
bitmap = wx.Bitmap('ace2.png', wx.BITMAP_TYPE_PNG)
image = wx.StaticBitmap(panel1, -1, bitmap)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(panel1)
self.SetSizerAndFit(sizer)
self.Show()
self.OnTransparent(None)
def OnTransparent(self, event):
self.SetTransparent(180)
self.Update()
app = wx.App()
frame = MyFrame(None, -1)
app.MainLoop()
Try something like this:
import tkinter as tk
root = tk.Tk()
root.overrideredirect(True)
root.config(bg="blue", bd=0, highlightthickness=0)
root.attributes("-transparentcolor", "blue")
canvas = tk.Canvas(root, bg="blue", bd=0, highlightthickness=0)
canvas.pack()
tk_img = tk.PhotoImage(file="cards.png")
canvas.create_image(0, 0, image=tk_img, anchor="nw")
root.mainloop()
It creates an image on the screen like this:
It changes the transparent colour of the window to "blue" (because there isn't any thing blue in the picture that I am using) and sets the background colour to "blue". That makes the window transparent. To show the image I used a tk.Canvas but it might work with tk.Label (haven't tested it).
Please note that this will most likely work only on Windows.
Related
I'm trying to display an image on the screen, without any window/application popping up/containing it. I'm pretty close with TKinter, but the method for removing the background color of the canvas is hacky and has some undesired effects.
import tkinter as tk
import ctypes
user32 = ctypes.windll.user32
screen_size = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
root = tk.Tk()
root.overrideredirect(True)
root.config(bg="blue", bd=0, highlightthickness=0)
root.attributes("-transparentcolor", "#FEFCFD")
root.attributes("-topmost", True)
tk_img = tk.PhotoImage(file="image.png")
canvas = tk.Canvas(root, bg="#FEFCFD", bd=0, highlightthickness=0, width=screen_size[0], height=screen_size[1])
canvas.pack()
img = canvas.create_image(0, 0, image=tk_img, anchor="nw")
root.mainloop()
The -transparentcolor flag mostly removes the background, but if an image has any partially transparent pixels it will tint them. Plus, if that color exists in the image, it will be removed; that choice of color was in hopes of minimizing exact matches in an image while also being mostly white, to hopefully have the least noticeable affect on the images. Here's an image of what it looks like currently; very close to what I want, but you can see some missing pixels in the white areas of the dice, and they all seem to have a white border around them due to their edges being partially transparent. This is what the image should look like.
I've also tried to achieve this effect using wxPython, but I can't remove the background of the window, leading to transparent images always being backed by some color. I used this answer; I've modified it slightly but nothing I've done has improved it.
So, is there a way to draw an image on the screen without any background at all with Python?
Thanks to the suggestion from Kartikeya, I was able to solve my own question.
Using PyQt5, this code will display an image with transparency and no border or background at all
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
app = QApplication(sys.argv)
window = QMainWindow()
window.setAttribute(Qt.WA_TranslucentBackground, True)
window.setAttribute(Qt.WA_NoSystemBackground, True)
window.setWindowFlags(Qt.FramelessWindowHint)
label = QLabel(window)
pixmap = QPixmap('image.png')
label.setPixmap(pixmap)
label.setGeometry(0, 0, pixmap.width(), pixmap.height())
window.label = label
window.resize(pixmap.width(),pixmap.height())
window.show()
sys.exit(app.exec_())
Once I was looking for PyQt5, I found this question and only needed to modify the code slightly. Here is what it looks like now.
So, is there a way to draw an image on the screen without any
background at all with Python?
Using Tkinter, for this image, no, you cannot achieve the desired result. (You can look for other modules like 'PyQT5', 'Kivy', 'wxPython', or 'turtle' maybe.)
See, transparentcolor Specifies the transparent color index of the toplevel.
If you want to do the best in Tkinter, here are some changes to your code:
root.attributes('-transparentcolor', '#d4d4e2')
root.attributes("-topmost", True)
tk_img = tk.PhotoImage(file="image.png")
canvas = tk.Canvas(root, bg="#d4d4e2", bd=0, highlightthickness=0, width=screen_size[0], height=screen_size[1])
So, this will display a window that contains a canvas with transparent background. Very close to what you wanted, you can see very less missing pixels in the white areas of the dice, but still, this isn't the solution.
but if an image has any partially transparent pixels it will tint them
Yes, it's true, if that color exists in the image, it will be removed, as that color is being used to mark what needs to be used as transparent color.
that choice of color was in hopes of minimizing exact matches in an
image while also being mostly white, to hopefully have the least
noticeable effect on the images.
For the edges/borders of this image to retain the partially 'white' transparent background, the choice of color needs to be some shade of 'white'. So, the color used to make it transparent is #d4d4e2 (For this image, there was only one place where this color pixel was used, so it goes unnoticable.) Still, the edges will have sharp corners and cuts.
I am working on a tkinter project, where I set the background image (not a solid color). I was using .grid() for my widgets, but then I realized I need to display a widget at the very bottom of the tkinter window. To continue using grid for this, I separated my widgets into two frames; 1 frame that contained most of the widgets and one that just contained the widget I want to be displayed at the bottom. Then, I packed my frames, one on the top and the other at the bottom.
However, after I did this, my background image went on top of my existing widgets and blocked them out, so they can't be seen anymore. What can I do to fix this?
Here is the method I used to display the background photo:
from tkinter import *
filename = PhotoImage(file="image.png")
background_label = Label(root, image=filename) # root is the Tk() object
background_label.place(x=0, y=0, relwidth=1, relheight=1)
# Other code here
mainloop()
The problem is if you run this code below and resize the window then you will get
unexpected behavior. Widgets are not rendered like they should and the backgrounds
have some wierd colors.
I know if you set a argument then you should
fill it with a valide value. If you do fill it with an empty string like I did
it don't throw a <_tkinter.TclError: unknown color name "">.
My Question is why we
don't get an error? Is that a tkinter bug? Because if you set the bg argument of Canvas
as an empty string you will get an error: unknown color name ""
from tkinter import Tk, Frame, Button, Entry, Canvas
class Gui(Frame):
def __init__(self, master):
self.master = master
self.font1 = font=("Segoe UI",18,"bold")
self.font2 = font=("Segoe UI",28,"bold")
Frame.__init__(self, self.master, bg="")
self.grid()
self.create_widgets()
def create_widgets(self):
self.frame_container = Frame(self, bg="")
self.frame_container.grid(row=0, column=0)
self.button_test_1 = Button(self, text="Test1", bg="yellow")
self.button_test_1.grid()
self.can = Canvas(self.frame_container, bg="orchid1")
self.can.grid(row=0, column=0)
self.entry_test = Entry(self.frame_container)
self.entry_test.grid(row=0, column=1)
if __name__ == "__main__":
root = Tk()
bug_gui = Gui(root)
root.mainloop()
#Idlehands Thanks for citing Fredrik Lundh's effbot.org explanation on background. I think I finally understand Fredrik Lundh's documentation. Namely,
bg="" is a valid argument that will prevent the updating of the Frame widget's background, and
the background or bg of a Frame widget defaults to the application background color.
Hence, when the Tk window is resized, e.g. when the right edge of the Tk window is move to the left to reach the Canvas widget's right edge and followed by moving right back to its original position, the following phenomenon happens:
Firstly, the Frame widget's width reduces in size. When this happens, I suspect the Tk widow background colour that filled the Frame widget is lost.
Secondly, when the Frame widget's width next returns to its original size, it's background needs to be updated (i.e. filled) with a defined colour. Because bg="" prevents updating, i.e. prevents filling the Frame widget background with a defined background colour, the Frame widget's background will default to the "application background colour". Meaning it will be filled with the exact color of the area directly behind the Frame widget and the Tk window, as if it became 'transparent'.
Answering #Turjak_art question, tkinter did not yield an Error or Exception because bg="" is a valid argument of the Frame widget.
Edit:
Image showing Frame background with fast resizing.
Image showing Frame background with slow resizing.
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.
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
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()
Explaination:
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 hexadezimal 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.
import wx
class DrawPanel(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,title="Draw",size=(150,150))
self.SetTransparent(0)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, event=None):
dc = wx.PaintDC(self)
dc.SetPen(wx.Pen(wx.BLACK, 5))
# Draw a line
dc.DrawLine(0, 0, 150, 150)
app = wx.App()
frame = DrawPanel()
frame.Show(True)
app.MainLoop()
Now frame and line are all invisible.
Please tell me how to make frame fully transparent, while line is visible ?
When you make the frame transparent, it makes all its children transparent too. Thus, this really isn't possible. You can make semi-transparent drawings though via GraphicsContext. And you could take a look at GLCanvas (a wrapper for OpenGL) or you might be able to cobble something together with Cairo. See the wxPython demo package (available from www.wxpython.org) for examples.