我正在尝试使用 Tkinter 创建透明水印叠加层,在所有显示器上显示系统信息(主机名、用户名、IP 和时间)。水印每秒更新一次,根本不应该干扰用户交互。
我面临的问题: 该窗口捕获鼠标点击,阻止与其下方的应用程序交互。 文本有时会干扰交互,这意味着它并不总是完全透明。
import socket
import psutil
import tkinter as tk
from datetime import datetime
from screeninfo import get_monitors
def get_system_info():
hostname = socket.gethostname()
user = psutil.users()[0].name
ip = socket.gethostbyname(hostname)
current_time = datetime.now().strftime("%y-%m-%d %H:%M")
return f"{hostname},{current_time}, {user} "
def display_watermark(text):
windows = []
for monitor in get_monitors():
root = tk.Tk()
root.overrideredirect(True)
root.attributes("-topmost", True)
root.attributes("-alpha", 0.3)
root.geometry(f"{monitor.width}x{monitor.height}+{monitor.x}+{monitor.y}")
root.config(bg="black")
root.wm_attributes("-transparentcolor", "#000000")
root.protocol("WM_DELETE_WINDOW", lambda: None)
canvas = tk.Canvas(root, width=monitor.width, height=monitor.height, bg="black", highlightthickness=0)
canvas.pack(fill="both", expand=True)
for y in range(-50, monitor.height + 100, 400):
for x in range(-100, monitor.width + 200, 800):
canvas.create_text(x, y, text=text, font=("Arial", 28), fill="gray", angle=45)
windows.append((root, canvas, monitor))
def update_text():
new_text = get_system_info()
for window, canvas, monitor in windows:
canvas.delete("all")
for y in range(-50, monitor.height + 100, 400):
for x in range(-100, monitor.width + 200, 800):
canvas.create_text(x, y, text=new_text, font=("Arial", 28), fill="gray", angle=45)
root.after(1000, update_text)
update_text()
for window, _, _ in windows:
window.mainloop()
system_info_text = get_system_info()
display_watermark(system_info_text)
我尝试通过以下方式解决它
root.attributes("-alpha", 0.3) – 使窗口半透明,但仍然阻止点击。 root.wm_attributes("-transparentcolor", "#000000") – 使背景透明,但文本仍然捕获点击。 root.attributes("-disabled", True) – 禁用窗口,但也阻止更新水印文本。 使用 root.lower() 和 root.attributes("-topmost", False),但水印会隐藏在其他窗口后面。
import socket
import psutil
import tkinter as tk
from datetime import datetime
from screeninfo import get_monitors
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 get_system_info():
hostname = socket.gethostname()
user = psutil.users()[0].name
current_time = datetime.now().strftime("%y-%m-%d %H:%M")
return f"{hostname}, {current_time}, {user}"
def display_watermark():
monitors = get_monitors()
min_x = min(m.x for m in monitors)
min_y = min(m.y for m in monitors)
max_width = sum(m.width for m in monitors)
max_height = max(m.y + m.height for m in monitors) - min_y
root = tk.Tk()
root.overrideredirect(True)
root.attributes("-topmost", True)
root.attributes("-alpha", 0.3)
root.geometry(f"{max_width}x{max_height}+{min_x}+{min_y}")
root.config(bg="black")
root.wm_attributes("-transparentcolor", "#000000")
canvas = tk.Canvas(root, width=max_width, height=max_height, bg="black", highlightthickness=0)
setClickthrough(canvas)
canvas.pack(fill="both", expand=True)
def update_text():
new_text = get_system_info()
canvas.delete("all")
for y in range(-50, max_height + 100, 400):
for x in range(-100, max_width + 200, 800):
canvas.create_text(x, y, text=new_text, font=("Arial", 28), fill="gray", angle=45)
root.after(1000, update_text)
update_text()
root.mainloop()
display_watermark()
水印应该完全点击通过(无交互阻塞)。 文本也应该是透明的并且不会干扰鼠标点击。 如何确保整个窗口,包括文字,完全可以点击通过?
您可以使用内置的
ctypes.windll
代替 win32con
和 win32gui
,并将 GetWindowLongW()
和 SetWindowLongW()
应用在窗口上,而不是画布上。
您也可以简单地创建一个覆盖所有显示器的窗口。
以下是根据您的修改后的示例:
from ctypes import windll
from datetime import datetime
import os
import socket
import tkinter as tk
from screeninfo import get_monitors
# required constants
WS_EX_LAYERED = 0x00080000
WS_EX_TRANSPARENT = 0x00000020
GWL_EXSTYLE = -20
# transparent color
COLOR = '#000000'
def set_click_through(win):
hwnd = windll.user32.GetParent(win.winfo_id())
ex_style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
ex_style |= WS_EX_TRANSPARENT | WS_EX_LAYERED
windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style)
def get_system_info():
hostname = socket.gethostname()
user = os.getlogin()
now = datetime.now().strftime('%F %H:%M')
return f'{hostname}, {now}, {user}'
def update_text(canvas):
new_text = get_system_info()
canvas.itemconfig('text', text=new_text)
canvas.after(1000, update_text, canvas)
def display_watermark():
# get the actual size including all monitors
monitors = get_monitors()
max_width = max(m.x+m.width for m in monitors)
max_height = max(m.y+m.height for m in monitors)
print(f'{max_width=} {max_height=}')
root = tk.Tk()
root.geometry(f'{max_width}x{max_height}+0+0')
root.overrideredirect(True)
root.attributes('-transparentcolor', COLOR, '-topmost', 1, '-alpha', 0.3)
canvas = tk.Canvas(root, bg=COLOR, highlightthickness=0)
canvas.pack(fill='both', expand=1)
for y in range(-50, max_height+100, 400):
for x in range(-100, max_width+200, 800):
canvas.create_text(x, y, font=('Arial', 28, 'bold'), fill='gray', angle=45, tags=('text',))
# call set_click_through after window is updated and ready
root.after(10, set_click_through, root)
root.after(20, update_text, canvas)
root.mainloop()
if __name__ == '__main__':
display_watermark()