Tony Qu
Tony Qu

Reputation: 61

Click-through tkinter windows

The function is copied from Tying to set non-interactable (click-through) overlay with TkInter

Not only is the window not click through, the png is also not transparent. PNG is here: https://drive.google.com/file/d/1tlLl2hjPq38mc_c_PpMhkKDlP1HqvDY5/view

This is what the window looks like:

screenshot of window

What am I missing?

from tkinter import*
import win32gui

from win32gui import GetForegroundWindow, ShowWindow, FindWindow, SetWindowLong, GetWindowLong, SetLayeredWindowAttributes
from win32con import SW_MINIMIZE, WS_EX_LAYERED, WS_EX_TRANSPARENT, GWL_EXSTYLE

def setClickthrough(hwnd):
   try:
       styles = GetWindowLong(hwnd, GWL_EXSTYLE)
       styles |= WS_EX_LAYERED | WS_EX_TRANSPARENT
       SetWindowLong(hwnd, GWL_EXSTYLE, styles)
       SetLayeredWindowAttributes(hwnd, 0, 255, win32con.LWA_ALPHA)
   except Exception as e:
       print(e)

root = Tk()
root.geometry("100x100")


root.overrideredirect(1)

root.attributes('-topmost', 1)
pic = PhotoImage(file=r'on2.png')
root.wm_attributes("-transparentcolor", 'white')

boardbutton = Label(root, image=pic, bd=0,
                    bg='white')
boardbutton.pack()
setClickthrough(root.winfo_id())
root.mainloop()

Upvotes: 4

Views: 3831

Answers (2)

Thingamabobs
Thingamabobs

Reputation: 8037

I have took the code of the linked question and made it work. See code below:

from tkinter import *
from PIL import Image, ImageTk
import win32gui
import win32con

def setClickthrough(hwnd):
    print("setting window properties")
    try:
        styles = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
        styles = win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT
        win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, styles)
        win32gui.SetLayeredWindowAttributes(hwnd, 0, 255, win32con.LWA_ALPHA)
    except Exception as e:
        print(e)

# 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)
bg = Canvas(root, width=width, height=height, bg='white')

setClickthrough(bg.winfo_id())

frame = ImageTk.PhotoImage(file="example.png")
bg.create_image(1920/2, 1080/2, image=frame)
bg.pack()
root.mainloop()

The important difference between your try and the working example seems to be that there was in use the hwnd of a canvas instead of a window.


I'm not abel to do exactly what you wish for but I have provided some code that outputs me this and hopefully sadisfy your needs. The extra code just removes the decoration (overrideredirect(1)) and resizes the window to the img size, also it places it in the middle of the screen.:

enter image description here

from tkinter import *
from PIL import Image, ImageTk
import win32gui
import win32con

def setClickthrough(hwnd):
    print("setting window properties")
    try:
        styles = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
        styles = win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT
        win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, styles)
        win32gui.SetLayeredWindowAttributes(hwnd, 0, 255, win32con.LWA_ALPHA)
    except Exception as e:
        print(e)

def size_position_for_picture():
    bbox = bg.bbox(img_id)
    w,h = bbox[2]-bbox[0],bbox[3]-bbox[1]
    x,y = sw/2-w/2,sh/2-h/2
    root.geometry('%dx%d+%d+%d' % (w,h, x,y))
    bg.configure(width=w,height=h)
    

root = Tk()

sw = root.winfo_screenwidth()
sh = root.winfo_screenheight()

root.overrideredirect(1)
root.attributes("-alpha", 0.75)
root.attributes('-transparentcolor', 'white', '-topmost', 1)
bg = Canvas(root,bg='white',highlightthickness=0)
root.config(bg='white')

setClickthrough(bg.winfo_id())

frame = ImageTk.PhotoImage(file="example.png")
img_id = bg.create_image(0,0, image=frame,anchor='nw')
bg.pack()

size_position_for_picture()
setClickthrough(bg.winfo_id())


root.mainloop()

Upvotes: 6

oskros
oskros

Reputation: 3285

I have a function doing this for an application of mine as well, the code looks very similar to yours with some minor tweaks. You can try it out:

def set_clickthrough(hwnd, root):
    # Get window style and perform a 'bitwise or' operation to make the style layered and transparent, achieving
    # the clickthrough property
    l_ex_style = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
    l_ex_style |= win32con.WS_EX_TRANSPARENT | win32con.WS_EX_LAYERED
    win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, l_ex_style)

    # Set the window to be transparent and appear always on top
    win32gui.SetLayeredWindowAttributes(hwnd, win32api.RGB(0, 0, 0), 190, win32con.LWA_ALPHA)  # transparent
    win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, root.winfo_x(), root.winfo_y(), 0, 0, 0)

And also, the following function to disable the clickthrough again

def disable_clickthrough(hwnd, root):
    # Calling the function again sets the extended style of the window to zero, reverting to a standard window
    win32api.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, 0)
    # Remove the always on top property again, in case always on top was set to false in options
    win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, root.winfo_x(), root.winfo_y(), 0, 0, 0)

You can get the window handle (hwnd) by calling win32gui.FindWindow(None, root.title()).

Upvotes: 3

Related Questions