Tying to set non-interactable (click-through) overlay with TkInter - python

I've gone through several other posts regarding similar issues and all seem to point to this code. Since, I'm working on creating a desktop overlay, I setup a short program to create a top window and hopefully isolate the issue but, despite my efforts I can't figure out why this seemingly widely accepted solution is failing for me or what I could be missing.
I've verified that the handle being retrieved is referencing the correct window, trimmed away unnecessary functions, and explored other variations of setting window styles through personal trial and error and mimicking some examples found through programcreek.com.
from tkinter import *
from PIL import Image, ImageTk
from win32gui import GetForegroundWindow, ShowWindow, FindWindow,
SetWindowLong, GetWindowLong
from win32con import SW_MINIMIZE, WS_EX_LAYERED, WS_EX_TRANSPARENT, GWL_EXSTYLE
def setClickthrough(root, window="Applepie"):
hwnd = FindWindow(None, window)
styles = GetWindowLong(hwnd, GWL_EXSTYLE)
styles |= WS_EX_LAYERED | WS_EX_TRANSPARENT
print(SetWindowLong(hwnd, GWL_EXSTYLE, styles))
# Dimensions
width = 1920 #self.winfo_screenwidth()
height = 1080 #self.winfo_screenheight()
root = Tk()
root.geometry('%dx%d' % (width, height))
root.title("Applepie")
root.attributes('-transparentcolor', 'white', '-topmost', 1)
root.config(bg='white')
root.attributes("-alpha", 0.25)
root.wm_attributes("-topmost", 1)
root.bg = Canvas(root, width=width, height=height, bg='white')
setClickthrough(root)
frame = ImageTk.PhotoImage(file="Resources/Test/Test-0000.gif")
root.bg.create_image(1920/2, 1080/2, image=frame)
root.bg.pack()
root.mainloop()
The TkInter window was successfully made transparent and clickable-through using the solution provided by acw1668 using:
root.attributes('-transparentcolor', 'white', '-topmost', 1)
root.config(bg='white')
root.bg = Canvas(root, width=width, height=height, bg='white')
Problem persists with creating an image in the canvas. Need to be able to have additional images be clickable through as well:
frame = ImageTk.PhotoImage(file="Resources/Test/Test-0000.gif")
root.bg.create_image(1920/2, 1080/2, image=frame)

Turns out the handle was not being captured properly using FindWindow and using alternatives such as root.frame() or root.winfo_id() did not match the handle of the window for some reason. By passing in the winfo_id() of the Canvas, I was able to get the below code to work:
self.root.config(bg='#000000')
self.root.wm_attributes("-topmost", 1)
self.root.attributes('-transparentcolor', '#000000', '-topmost', 1)
print("Configuring bg")
self.bg = Canvas(self.root, highlightthickness=0)
self.setClickthrough(self.bg.winfo_id())
Calling:
def setClickthrough(self, hwnd):
print("setting window properties")
try:
styles = GetWindowLong(hwnd, GWL_EXSTYLE)
styles = WS_EX_LAYERED | WS_EX_TRANSPARENT
SetWindowLong(hwnd, GWL_EXSTYLE, styles)
SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA)
except Exception as e:
print(e)

Related

Tkinter performance degrades when I change PNG files but I can't figure out why

I've been learning some basic python gui stuff for a project, I've created an animation for an RPM dial, when I use the first PNG exported from Photoshop, it works fine. However when I add more features to the dial in PS and then use that PNG exported with the same settings the performance degrades noticeably. The file sizes are the same on paper but clearly not in reality, I'm wondering if there's a workaround in the code or export.
The python script
import tkinter as tk
from tkinter.constants import CENTER
from PIL import ImageTk,Image
# --- Main application set up ---
main_app = tk.Tk()
main_app.title('DT1')
width_value=main_app.winfo_screenwidth()
height_value=main_app.winfo_screenheight()
#width_value=1400
#height_value=1000
#main_app.geometry("%dx%d+0.5+0.5" % (width_value, height_value))
main_app.geometry("%dx%d" % (width_value, height_value))
# Frame setup
frame = tk.Frame(main_app, bg='black')
frame.place(relwidth=1, relheight=1)
# DES Logo
DES_logo=tk.Label(frame, text='DT1', font='ariel 18 bold', bg='#000000', fg='#626262')
DES_logo.place(relx=0.01, rely=0.91, relwidth=0.20, relheight=0.05)
#Canvas
my_canvas2 = tk.Canvas(frame, width = 800, height = 120, background = 'black', highlightthickness=0)
my_canvas2.place(x=10, y=10)
img = ImageTk.PhotoImage(Image.open(r'C:/Users/Sandip Dhillon/Desktop/stuff/dev_tests/img/dialtest3.png'))
my_canvas3 = tk.Canvas(frame, width = 1920, height = 1080, background = 'black', highlightthickness=0)
my_canvas3.place(x=1, y=1)
my_canvas3.create_image(960, 540, anchor=CENTER, image=img, tags="image")
class Dial():
count = 200
upper_limit = 359
#staticmethod
def add_Dialcount():
if Dial.count < Dial.upper_limit :
Dial.count -= 3
print(Dial.count)
CanvasCreate.canvas_Handling()
else:
Dial.count = 200
print(Dial.count)
CanvasCreate.canvas_Handling()
class CanvasCreate():
#staticmethod
def canvas_Handling():
# Clear Canvas
my_canvas3.delete("dial")
# Create RPM Line/bar
my_canvas3.create_arc(580,160,1340,930, extent=Dial.count, fill='#a15800', tags="dial")
my_canvas3.tag_raise("image")
# Call for the loop to start again
main_app.after(10,Dial.add_Dialcount)
Dial.add_Dialcount()
main_app.mainloop()
When I use this png it works perfectly
PNG1
However when I use this one it's noticeably slower
PNG2
And a screencap of the file sizes, the first PNG's actually a smaller file
File Sizes PNG2 & PN1
I thought maybe a workaround would be to have a transparent canvas and then place the PNG on top of that so I didn't have to raise it every loop, but I don't think you can make a canvas transparent.
PS the dial counter logic is broken but it was a quick and dirty test and doesn't effect this bug, will fix!
Thank you

Python: auto slideshow with PIL/TkInter with no saving images

I´m designing a slideshow with no user intervention, where:
a. Every image is generated by the Python script itself
b. There´s no file saving, for performance reasons
c. Every image is shown in fullscreen for a certain time
d. It´s a loop that´s supposed to never end. There´s always going to be an image to show
So far, by adapting code found in a few pages, I have it running. But every image is shown for X time and then the desktop background appears for a second or so.
I´d like to have a smooth switching from one file to next, such as FEH does. As a matter of fact, I´m trying to replace FEH because I need finer control of the display of each file (for instance, changing the time it appears on screen).
Here´s my code:
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
from PIL import ImageTk
import tkinter
def show_center(pil_image, msDelay):
root = tkinter.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.attributes("-topmost", True)
canvas = tkinter.Canvas(root, width=w, height=h, highlightthickness=0)
canvas.pack()
canvas.configure(background='black')
image = ImageTk.PhotoImage(pil_image)
imagesprite = canvas.create_image(w / 2, h / 2, image=image)
root.after(msDelay, root.destroy)
root.mainloop()
### script body
while True:
# loads common background image
img = Image.open(baseImage)
draw = ImageDraw.Draw(img)
# here: image customization
draw.rectangle(....)
draw.text(....)
img.paste(....)
# shows this file
thisDelay = some Number Calculation
show_center(img, thisDelay)
Any ideas on how to avoid the desktop appearing between images? This will run in a headless Raspberry. I´m using Python3 on Raspbian.
Thanks in advance!
You can use after() instead of the while loop and simply use Label instead of Canvas:
import tkinter as tk
from PIL import Image, ImageTk, ImageDraw, ImageFont
import time
import random
def update_image():
# sample drawing
image = base_image.copy()
draw = ImageDraw.Draw(image)
draw.rectangle((100, 100, 500, 400), outline=random.choice(('red', 'green', 'blue', 'magenta', 'gold', 'orange')))
draw.text((120, 120), f"""{time.strftime("%F %T")}""")
# update image
tkimg = ImageTk.PhotoImage(image)
label.config(image=tkimg)
label.image = tkimg # save a reference to avoid garbage collected
ms_delay = random.randint(1000, 9000) # sample delay calculation
root.after(ms_delay, update_image)
root = tk.Tk()
root.attributes("-fullscreen", 1, "-topmost", 1)
base_image = Image.open("/path/to/base/image")
# label for showing image
label = tk.Label(root, bg="black")
label.pack(fill="both", expand=1)
update_image() # start the slide show
root.mainloop()
Well, it´s working quite well.
The solution required a bit of logic (maybe it makes sense not to destroy the object for every image) and a bit of good old trial & error.
The changes were:
Init the canvas only once, use global vars to make it persistent
For every image, call the display function and keep calling root.update() until the required timeout is reached
So, the prior function gets divided, and it looks like:
global canvas, root
global w, h
def init_image():
global canvas, root
global w, h
root = tkinter.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.attributes("-topmost", True)
canvas = tkinter.Canvas(root, width=w, height=h, highlightthickness=0)
canvas.pack()
canvas.configure(background='black')
return
def show_center(pil_image, msDelay):
global canvas, root
global w, h
image = ImageTk.PhotoImage(pil_image)
imagesprite = canvas.create_image(w / 2, h / 2, image=image)
inicio = int(time.time() * 1000)
while 1:
root.update()
if (int(time.time() * 1000) - inicio) > msDelay:
break
return
Function init_image() is called once at beginning, and function show_center() is called for every image as the original post.
I hope this can be useful to anybody trying to accomplish the same.

Python with tkinter aspect ratio issue

I am attempting to create a simple window that maintains a square shape when resized, using python 3.6.4 and tkinter 8.6. Here is my code that produces a window, but does not maintain its aspect ratio when resized.
import tkinter as tk
w = tk.Tk()
w.aspect(1,1,1,1)
w.mainloop()
maybe you can use a canvas to make that, he detect event (image size changed) and .place relative x and relative y. I tryed make a script to help you, but you need make somes changes
from tkinter import *
# create a canvas with no internal border
canvas = Canvas(bd=0, highlightthickness=0)
canvas.pack(fill=BOTH, expand=1)
lastw, lasth = canvas.winfo_width(), canvas.winfo_height()
# track changes to the canvas size and draw
# a rectangle which fills the visible part of
# the canvas
def configure(event):
global lastw, lasth
canvas.delete("all")
w, h = event.width, event.height
try:
label.config(font = ('Arial ', int(12 * ((w - lastw) / (h - lasth))))) # -- this formula need change :3
except ZeroDivisionError: pass
lastw, lasth = canvas.winfo_width(), canvas.winfo_height()
canvas.bind("<Configure>", configure)
label = Label(canvas, text = "YOLO")
label.place(relx = 0.5, rely = 0.5) # - this make the widget automatic change her pos
mainloop()
you can see how this work here http://effbot.org/zone/tkinter-window-size.htm

Tkinter - Button Image Transparent Background

I have worked out how to have an image for a button, thats positioned on top of a label (I think I maybe doing it the long-winded way because I can't install PIL on my Mac for some reason). Anyhow, it works as it should to a certain degree - the problem I have is it's adding white space either side, and then the image itself is not displaying its transparent background.
The code I am using is as follows:
from tkinter import *
#from PIL import Image
root = Tk()
#Removes the title bar and places over mac top bar
root.tk.call("::tk::unsupported::MacWindowStyle", "style", root._w, "plain", "none")
# Makes the app full screen
#root.wm_attributes('-fullscreen', 1)
root.geometry('{}x{}'.format(480, 320))
#root.attributes('-topmost', True)
def quitApp():
# mlabel = Label (root, text = 'Close').pack()
root.destroy()
background_img = PhotoImage(file="images/bg.gif")
scanBtn_img = PhotoImage(file="images/scanBtn.gif")
background = Label(root,
compound = CENTER,
quitButton = Button(image=scanBtn_img, command = quitApp).pack(),
image = background_img).pack(side="right")
background.image = background_img # keep a reference!
root.mainloop()
From what I understand tkinter natively supports transparency on images like GIF.
I chopped up your code a little but it does work for me. Maybe there is a problem with how you have set up your code. Your label also has a button in it. I don't think you need to have both. You can just created the button where you want it.
Just for reference I created a Label and a Button packed on different sides with a black background to show the transparency of the image.
Here is the code I used to test a gif I have that has transparency. I tested this on both python 3.6 and 2.7 just in case.
from tkinter import *
root = Tk()
def quitApp():
root.destroy()
background_img = PhotoImage(file="Colors/sa.gif")
scanBtn_img = PhotoImage(file="Colors/sa.gif")
background = Label(root,bg='black', image = background_img).pack(side = RIGHT)
quitButton = Button(bg='black', image=scanBtn_img, command = quitApp).pack(side = LEFT)
backgroundimage = background_img # keep a reference!
root.mainloop()
Update: I used the gif you link in the comment
Here is the result.
Update:
After doing some more digging I found what might work for Mac OS:
I don't have a Mac to test on right now so let me know if this works for you:
from tkinter import *
root = Tk()
# Hide the root window drag bar and close button
root.overrideredirect(True)
# Make the root window always on top
root.wm_attributes("-topmost", True)
# Make the window content area transparent
root.wm_attributes("-transparent", True)
# Set the root window background color to a transparent color
root.config(bg='systemTransparent')
def quitApp():
root.destroy()
background_img = PhotoImage(file="Colors/1.gif")
scanBtn_img = PhotoImage(file="Colors/1.gif")
background = Label(root,bg='black', image = background_img)
background.pack(side = RIGHT)
background.config(bg='systemTransparent')
quitButton = Button(bg='black', image=scanBtn_img, command = quitApp)
quitButton.pack(side = LEFT)
quitButton.config(bg='systemTransparent')
backgroundimage = background_img # keep a reference!
root.mainloop()

How to make a tkinter Label background transparent?

I have a window with a label as my frame. I did this because i wanted an image in the background. But now im having trouble with the other labels i have used. The other labels i have used to actually labeled things dont have a transparent background. Is there a way to make the background of these labels transparent?
import Tkinter as tk
root = tk.Tk()
root.title('background image')
image1 = Tk.PhotoImage(file='image_name.gif')
# get the image size
w = image1.width()
h = image1.height()
# make the root window the size of the image
root.geometry("%dx%d" % (w, h))
# root has no image argument, so use a label as a panel
panel1 = tk.Label(root, image=image1)
panel1.pack(side='top', fill='both', expand='yes')
# put a button/label on the image panel to test it
label1 = tk.Label(panel1, text='here i am')
label1.pack(side=Top)
button2 = tk.Button(panel1, text='button2')
button2.pack(side='top')
# start the event loop
root.mainloop()
i think it can help, all black will be transparent
root.wm_attributes('-transparentcolor','black')
It is not supported with transparent backgrounds in Tk.
If you are working with images and putting text onto them, the most convenient way is - I think - utilizing Canvas widget.
tkinter Canvas widget has methods as .create_image(x, y, image=image, options) and .create_text(x, y, text="Some text", options).
use this :
from tkinter import *
main=Tk()
photo=PhotoImage(file='test.png')
Label(main,image=photo,bg='grey').pack()
#your other label or button or ...
main.wm_attributes("-transparentcolor", 'grey')
main.mainloop()
this work if use bg='grey' you can change it in line 7
good luck :)

Categories

Resources