self._window = tkinter.Tk()
self._window.option_add("*background", "green")
self._window.configure(background='green')
How can I instead of saying self._window.configure(background='green') say background = <something else>, maybe a tkinter.Canvas with drawings on it?
Just add a canvas that covers the whole window:
self.background = tkinter.Canvas(self._window)
self.background.pack(fill="both", expand=True)
Once you do that, you can draw anything you want on the background. From this point forward, just make sure all other widgets are children of the canvas rather than the root window.
Related
python 3.10, 3.11, windows 10
short:
transparency is affecting title bar when it shouldn't, simple code with example below, move the window to the middle of the screen, maximise and restore to reproduce the behaviour
long:
I know it looks so simple, but please bear with me. This thing is driving me nuts. I'm not sure what I could be doing wrong, it looks like a bug, maybe?
Maximising (or restoring to normal state after maximise) breaks the title bar. Title bar is not registering mouse clicks, I can't close the window or resize it because click goes through. It acts as it if it was transparent. In the main app I am using another hidden window with -alpha to grab mouse events on transparent canvas. Both windows are bound together and act as one. Having second window behind this one also doesn't help. Both windows are affected and unclickable (well, -alpha part of the window is clickable, but not the title bar).
I made a very short code to reproduce this behaviour. You can try it with frame instead of a canvas, or a button. Result is the same. Curious thing I have also discovered when using #000000 for colour - it breaks the window in even weirder way. Not sure what to make of it. #000001 works the same as yellow (or blue, red, etc.).
example:
from tkinter import Canvas, Tk
root = Tk()
root.attributes('-transparentcolor', 'yellow', '-topmost', True)
root.geometry("600x600")
x = Canvas(root, bg='yellow')
x.pack(expand=1, fill='both')
root.mainloop()
If I try different width and height values, sometimes I can grab only half of the title bar or close the window, but not minimise. It seems to depend on where the window is on the screen and the size of the object. Feels like the transparent part of the object is extending through the title bar making it unresponsive. I tried separating the title bar from the rest of the window with frames or shapes but it doesn't help (sometimes it works, but is dependent on size and location of the window - you may get lucky and not notice the behaviour)
x = Canvas(root, bg='yellow', height=600, width=600)
x.pack()
The best solution I've come up with so far:
def refresh(self):
self.state('iconic')
if self.state() == 'iconic':
self.state('normal')
self.focus_force() # needed for Entry widget
This function assigned to the button which is minimising the window to the taskbar and then returning it to normal state immediately. Obviously this is far from elegant, because the user have to perform an unnecessary action. I could use overrideredirect and hopefully recreate resize and close functionality of the window but it seems like an overkill for rather simple app.
Not sure what else to say. It's late, bye
edit:
trying this now and it somehow works, but sometimes the window blinks uncomfortably.
from tkinter import Canvas, Tk
class Window(Tk):
def __init__(self):
super().__init__()
self.bind("<Map>", self.refresh)
self.canv = Canvas(self)
self.canv.pack(expand=1, fill='both')
def refresh(self, event):
if self.state() == 'normal':
self.attributes('-transparentcolor', 'yellow', '-topmost', True)
self.canv.configure(bg='yellow')
elif self.state() == 'zoomed':
self.attributes('-transparentcolor', 'blue', '-topmost', True)
self.canv.configure(bg='blue')
if __name__ == '__main__':
w = Window()
w.mainloop()
I want to have a horizontal scrollbar for a canvas in tkinter. My canvas has both buttons and lines. When I use canvas inbuilt scrollbar it only scrolls the lines not the buttons. So for the buttons I tried using this function which shifts the position of buttons whenever the scrollbar is moved. But now the problem is that buttons and lines move with a different speed according to the width of the scrollbar. The scrollbar gives relative coordinates for its ends but I need absolute coordinates otherwise it disturbs the dragging feature of the buttons and lines. I have been stuck on this for a week now and can't figure out anything useful. Tried almost everything on stackoverflow.
Toplevel1.hbar = ttk.Scrollbar(panel_2, orient="horizontal")
self.SystemCanvas.configure(scrollregion = (0, 0, 1000, 1000),
xscrollcommand = Toplevel1.hbar.set)
Toplevel1.hbar['command'] = self.SystemCanvas.xview
Toplevel1.hbar.bind('<B1-Motion>', lambda e:hscrollBarMove(e,
Toplevel1.hbar, self.SystemCanvas))
Toplevel1.hbar.pack(side = "bottom", fill = "x")
def hscrollBarMove(event, hbar, systemcanvas):
for instance in systemcanvas.winfo_children():
if isinstance(instance, tk.Button) or isinstance(instance, tk.Menu):
try:
instance.place(relx = - hbar.get()[0])
except:
pass
Buttons and lines come dynamically.
Please someone help me :(
My canvas has both buttons and lines. When I use canvas inbuilt scrollbar it only scrolls the lines not the buttons.
Yes, that is correct. The canvas can only scroll canvas objects -- objects created with the various create_* methods (create_line, create_window, etc). It won't scroll items added to the canvas with place, pack, or grid.
If you want to put buttons on a canvas and have them be scrollable, use create_window to add them to the canvas.
I have a canvas with a background image in Tkinter. I want to add a frame with no background so that I can arrange elements in the window, but, still see the background behind these elements. When I use something similar to the code below, i.e., without specifying the bg color, I get a frame with a grey background. How do I turn it to no background at all?
import tkinter as tk
from PIL import Image,ImageTk
root=tk.Tk()
root.geometry("800x560")
bgImg=Image.open("data/bg.png")
bgImg=ImageTk.PhotoImage(bgImg)
canvas=tk.Canvas(root,width=800,height=560)
canvas.pack(expand = False, fill = "both")
canvas.create_image(0, 0, image=bgImg, anchor="nw")
frame=tk.Frame(canvas,width=50,height=50)
frame.place(relx=0.5,rely=0.5,anchor="center")
root.mainloop()
There may* be platform-dependent options, like this.
(* In Lubuntu 18.04 & python 3.6.9, they don't seem to work,
so I can't test them.)
One cross-platform(?) option, is to use Canvas.
Then draw images* on the canvas, instead of using widgets.
(*whose alpha channel, determines their transparent pixels)
This will draw foreground/background elements correctly,
& Canvas has enough functionality to control elements on it
(but will need work, if you want to emulate full widgets).
Canvas offers various create_xyz methods, where xyz:
arc, line, rectangle, bitmap, oval, text, image, polygon,
window (<- read 'widget')*.
These return an id, that represents the item in the canvas.
Items can also be associated in groups, represented by tags.
* When using widgets in Canvas, there are some restrictions:
https://tcl.tk/man/tcl8.6/TkCmd/canvas.htm#M163
"Note: due to restrictions in the ways that windows are managed,
it is not possible to draw other graphical items
(such as lines and images) on top of window items.
A window item always obscures any graphics that overlap it,
regardless of their order in the display list."
Items in a canvas, can be configured & have event-handlers:
itemconfig / itemcget
tag_bind / tag_unbind
These can use either an individual item-id, or* a group-tag.
(* despite the 'tag_' in the name, they also take item-ids)
Many other Canvas methods work with item-ids or group-tags
(e.g. move, delete,).
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=300)
canvas.pack()
# Note:
# If the images overlap exactly (same position & extent),
# the bottom one will never? get any events.
# Maybe events can be propagated, not sure right now.
fgImg = tk.PhotoImage(master=root, file='media/fg.png')
fgImgId = canvas.create_image(
80, 80, anchor=tk.NW, image=fgImg
)
canvas.tag_bind(
fgImgId,
'<ButtonRelease-1>',
lambda e: canvas.tag_raise(fgImgId)
)
bgImg = tk.PhotoImage(master=root, file='media/bg.png')
bgImgId = canvas.create_image(
0, 0, anchor=tk.NW, image=bgImg
)
canvas.tag_bind(
bgImgId,
'<ButtonRelease-1>',
lambda e: canvas.tag_raise(bgImgId)
)
root.mainloop()
There are more StackOverflow questions about this, e.g.:
transparent-background-in-a-tkinter-window
python-tkinter-label-background-transparent
configure-tkinter-ttk-widgets-with-transparent-backgrounds-ttk-frame-background
and more.
You might also want to look into other gui toolkits.
(e.g. wxpython*, pyqt, )
(*Here it says, that it has a SetTransparent command.)
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.
I am creating a canvas that overlays everything else on the screen using a widget without a window that is lifted with the topmost attribute.
However, I would like a transparent background.
Here is what I've got:
import Tkinter
vsize = vw, vh = 600, 350
w = Tkinter.Canvas(width=vw, height=vh, highlightthickness=0)
w.master.overrideredirect(True)
w.master.geometry("+0+0")
w.master.lift()
w.master.wm_attributes("-topmost", True)
w.master.wm_attributes("-disabled", True)
w.create_rectangle(0, 0, vw, vh, fill="black")
w.pack()
w.mainloop()
I tried adding the following attribute to the canvas and adding stipple="gray25" to the rectangle:
w.master.wm_attributes("-transparentcolor", "gray")
This for some reason didn't make the gray transparent, and even if this did work it would look awful using stipple.
Does anyone have any ideas on how to achieve this?
EDIT:
I tried configuring the background as black, and giving it some transparency, but I would only like the background to be black so I can have non-transparent items inside of the canvas.
w.configure(background="black")
w.master.wm_attributes("-alpha", 0.5)
There is currently no way to achieve this in Tkinter I believe.
If you are looking for a way to overlay things on your screen, why not use individual windows placed on the screen? Else, I would suggest looking at other solutions outside of Tkinter.