一 背景說明
? ? ? ? 網上看到一款Windows下的窗口透明化工具Glass2k(Glass2k官網),可以簡單地通過快捷鍵實現任意窗口的透明化,還挺方便的,想用Python自己實現一下類似的功能。
????????軟件已經開源到:窗口透明化小工具開源地址
? ? ? ? 效果圖如下:
二 設計實現
????????工具包含以下幾個模塊:
? ? ? ? 【1】用 tkinter 類實現工具的界面;
? ? ? ? 【2】用 pystray 類實現工具最小化托盤的功能;
? ? ? ? 【3】用pynput 類實現鍵盤快捷鍵的監聽功能;
? ? ? ? 【4】用ctypes 類實現Windows窗口透明化操作;
三 功能編寫
? ? ? ? 用GUI類包含設計中的幾種功能:
【1】初始化中實現工具的主界面:
def __init__(self):self.root = tk.Tk()self.root.title('窗口透明化工具')self.root.geometry("400x90")# 當用戶點擊窗口右上角的關閉按鈕時,Tkinter將自動發送WM_DELETE_WINDOW關閉事件。通過對其進行處理并調用self.hide_window()方法,可以改為將窗口隱藏到系統托盤中。# 該方法用于將程序窗口隱藏到系統托盤中而非直接退出應用程序self.root.protocol('WM_DELETE_WINDOW', self.hide_window)# 添加菜單和圖標self.create_systray_icon()# 繪制界面frame1 = tk.Frame(self.root)frame1.pack(side='top')l1 = tk.Label(frame1,text='【窗口透明化操作】點擊窗口,按Ctrl + Alt + [0-9]使其透明化。\nCtrl + Alt + 1:全透明\nCtrl + Alt + 5:半透明\nCtrl + Alt + 0:全填充\n')l1.pack()# 開啟鍵盤監聽t = threading.Thread(target=self.new_thread_start)# 開啟守護線程,這樣在GUI意外關閉時線程能正常退出t.setDaemon(True)t.start()
【2】最小化托盤包括:創建最小化托盤圖標/隱藏窗口/打開窗口/退出程序 幾個方法:
"""
[1]最小化托盤
"""
def create_systray_icon(self):# 使用 Pystray 創建系統托盤圖標menu = (pystray.MenuItem('顯示', self.show_window, default=True),pystray.Menu.SEPARATOR, # 在系統托盤菜單中添加分隔線pystray.MenuItem('退出', self.quit_window))image = Image.open("TPWin.ico")self.icon = pystray.Icon("icon", image, "圖標名稱", menu)threading.Thread(target=self.icon.run, daemon=True).start()def hide_window(self):# 關閉窗口時隱藏窗口,并將 Pystray 圖標放到系統托盤中self.root.withdraw()def show_window(self):# 打開主窗口self.icon.visible = Trueself.root.deiconify()def quit_window(self, icon: pystray.Icon):# 退出程序icon.stop() # 停止 Pystray 的事件循環self.root.quit() # 終止 Tkinter 的事件循環self.root.destroy() # 銷毀應用程序的主窗口和所有活動
【3】鍵盤的監聽包括:監聽鍵盤點擊/監聽鍵盤釋放/啟動監聽線程 幾個方法,其中組合鍵用一個無序不重復元素集來管理:
"""
[2]鍵盤操作
"""
def on_key_press(self, key):if key == keyboard.Key.alt_l or key == keyboard.Key.alt_gr:keys.add('Alt')elif key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:keys.add('Ctrl')elif str(key) == r"<48>": # ctrl + 0組合鍵keys.add('0')elif str(key) == r"<49>": # ctrl + 1組合鍵keys.add('1')elif str(key) == r"<50>": # ctrl + 2組合鍵keys.add('2')elif str(key) == r"<51>": # ctrl + 3組合鍵keys.add('3')elif str(key) == r"<52>": # ctrl + 4組合鍵keys.add('4')elif str(key) == r"<53>": # ctrl + 5組合鍵keys.add('5')elif str(key) == r"<54>": # ctrl + 6組合鍵keys.add('6')elif str(key) == r"<55>": # ctrl + 7組合鍵keys.add('7')elif str(key) == r"<56>": # ctrl + 8組合鍵keys.add('8')elif str(key) == r"<57>": # ctrl + 9組合鍵keys.add('9')if all(k in keys for k in ['Alt', 'Ctrl', '0']):self.set_transparency(0)elif all(k in keys for k in ['Alt', 'Ctrl', '1']):self.set_transparency(1)elif all(k in keys for k in ['Alt', 'Ctrl', '2']):self.set_transparency(2)elif all(k in keys for k in ['Alt', 'Ctrl', '3']):self.set_transparency(3)elif all(k in keys for k in ['Alt', 'Ctrl', '4']):self.set_transparency(4)elif all(k in keys for k in ['Alt', 'Ctrl', '5']):self.set_transparency(5)elif all(k in keys for k in ['Alt', 'Ctrl', '6']):self.set_transparency(6)elif all(k in keys for k in ['Alt', 'Ctrl', '7']):self.set_transparency(7)elif all(k in keys for k in ['Alt', 'Ctrl', '8']):self.set_transparency(8)elif all(k in keys for k in ['Alt', 'Ctrl', '9']):self.set_transparency(9)def on_key_release(self, key):if key == keyboard.Key.alt_l or key == keyboard.Key.alt_gr:keys.remove('Alt')elif key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:keys.remove('Ctrl')elif str(key) == r"<48>": # ctrl + 0組合鍵keys.remove('0')elif str(key) == r"<49>": # ctrl + 1組合鍵keys.remove('1')elif str(key) == r"<50>": # ctrl + 2組合鍵keys.remove('2')elif str(key) == r"<51>": # ctrl + 3組合鍵keys.remove('3')elif str(key) == r"<52>": # ctrl + 4組合鍵keys.remove('4')elif str(key) == r"<53>": # ctrl + 5組合鍵keys.remove('5')elif str(key) == r"<54>": # ctrl + 6組合鍵keys.remove('6')elif str(key) == r"<55>": # ctrl + 7組合鍵keys.remove('7')elif str(key) == r"<56>": # ctrl + 8組合鍵keys.remove('8')elif str(key) == r"<57>": # ctrl + 9組合鍵keys.remove('9')if key == keyboard.Key.esc:return False # 釋放了esc 鍵,停止監聽def new_thread_start(self):key_listen_thread = keyboard.Listener(on_press=self.on_key_press, on_release=self.on_key_release)# 運行線程key_listen_thread.start()
【4】設置透明度的操作包括:設置透明度/獲取當前鼠標位置(獲取窗口句柄用):
"""
[3]設置透明度操作(Ctrl + Alt + [0-9])
"""
def set_transparency(self, set_tp):global hwndhwnd = ctypes.windll.user32.WindowFromPoint(self.get_mouse_position()) # 獲取窗口句柄p = create_string_buffer(256)windll.user32.GetWindowTextW(hwnd, byref(p), 256) # 獲取窗口標題title = str(p.raw, encoding='utf-16').strip('\x00')exstyle = windll.user32.GetWindowLongA(hwnd, GWL_EXSTYLE)exstyle |= WS_EX_LAYERED # 使窗口具有能設置透明度的樣式windll.user32.SetWindowLongA(hwnd, GWL_EXSTYLE, exstyle) # 獲取窗口名if set_tp == 0:alpha = 255else:alpha = set_tp * 25windll.user32.SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA) # 設置透明度print('窗口 ' + title + ' 透明度設置為 ' + str(set_tp) + '\n')def get_mouse_position(self):point = ctypes.wintypes.POINT()ctypes.windll.user32.GetCursorPos(ctypes.byref(point))return point
四 程序整合
? ? ? ? 將上述功能整合:
import ctypes.wintypes
import threading
import tkinter as tk
import pystray
from PIL import Image
from pynput import keyboard
from ctypes import *WM_CLOSE = 0x10
WM_SETTEXT = 0x0c
GWL_STYLE = -16
GWL_EXSTYLE = -20
SW_MINIMIZE = 6
SW_MAXIMIZE = 3
SW_RESTORE = 9
WS_BORDER = 0x800000
WS_CAPTION = 0xC00000 # WS_BORDER Or WS_DLGFRAME
WS_CHILD = 0x40000000
WS_CLIPCHILDREN = 0x2000000
WS_CLIPSIBLINGS = 0x4000000
WS_POPUP = 0x80000000
WS_DLGFRAME = 0x400000
WS_DISABLED = 0x8000000
WS_OVERLAPPEDWINDOW = 0xcf0000
WS_THICKFRAME = 0x40000
WS_VISIBLE = 0x10000000
WS_EX_APPWINDOW = 0x40000
WS_EX_DLGMODALFRAME = 0x1
WS_EX_ACCEPTFILES = 0x10
WS_EX_CLIENTEDGE= 0x200
WS_EX_TOOLWINDOW = 0x80
WS_EX_WINDOWEDGE = 0x100
LWA_ALPHA = 0x2;LWA_COLORKEY=0x1
WS_EX_LAYERED = 0x80000keys = set()
hwnd = 0 # 窗口句柄class GUI:def __init__(self):#初始化窗口"""[1]最小化托盤""""""[2]鍵盤操作""""""[3]設置透明度操作(Ctrl + Alt + [0-9])"""if __name__ == '__main__':# 主界面TKDemo = GUI()TKDemo.root.mainloop()