使用Python來製作一個按鍵觸發Windows通知的指令碼
技術標籤:python
對於鍵盤沒有背光燈的同學而言,切換大小寫或控制Num鍵開關的時候沒有提示,經常需要試探性地輸入一些字元來判斷開關是否開啟,體驗非常糟糕。
因此,有人就想到自制指令碼這一招,一旦觸發大小寫切換或Num鍵切換就進行windows通知提示:
https://github.com/skate1512/Toggle_Keys_Notification
今天我們來試試這個指令碼,此外,我們還可以基於這個專案,擴充套件成任意一個按鍵被觸發或切換都進行 windows 通知的指令碼:
1.準備
開始之前,你要確保Python和pip已經成功安裝在電腦上,如果沒有,請訪問這篇文章:超詳細Python安裝指南
此外,推薦大家用VSCode編輯器,因為它可以在編輯器下方的終端執行命令安裝依賴模組:Python 程式設計的最好搭檔—VSCode 詳細指南。
Windows環境下開啟 Cmd (開始-執行-CMD),蘋果系統環境下請開啟 Terminal (command+空格輸入Terminal),輸入命令安裝依賴:
pip install win10toast
除此之外,我們需要下載作者的程式碼,GitHub地址:
https://github.com/skate1512/Toggle_Keys_Notification
2.原始碼使用與解析
2.1 原始碼使用
作者的專案可以在 Toggle_Keys_Notification 專案內,執行 notify.py 啟動監聽:
python notify.py
啟動後點擊一下大小寫切換鍵,觸發通知則說明程式碼正常運轉:
2.2 原始碼分析
該專案通過win32gui和win32con實現了彈出toast進行通知的功能,最核心的_show_toast程式碼位於 toast.py 中,下面是這個函式的部分程式碼剖析:
註冊和建立 window :
message_map = {WM_DESTROY: self.on_destroy, }
註冊Window
self.wc = WNDCLASS()
self.wc.lpszClassName = str("PythonTaskbar") # 定義該視窗結構的名稱
self.wc.lpfnWndProc = message_map
try:
self.classAtom = RegisterClass(self.wc)
except:
pass
Window格式
style = WS_OVERLAPPED | WS_SYSMENU
建立Window
self.hwnd = CreateWindow(self.classAtom, "Taskbar", style,
0, 0, CW_USEDEFAULT,
CW_USEDEFAULT,
0, 0, self.hinst, None)
UpdateWindow(self.hwnd)
所使用到的win32模組解析如下。
GetModuleHandle: 獲取一個應用程式或動態連結庫的模組控制代碼。
WM_DESTROY: 關閉程式。
RegisterClass: 將定義好的Window屬性儲存儲存下來。
WS_OVERLAPPED: 重疊式視窗,該式樣視窗 帶有一個標題欄和邊框。
WS_SYSMENU: 具有 SYSTEM 選單欄的樣式
CW_USEDEFAULT: 採用系統預設位置
CreateWindow 這個函式具有非常多的引數,甚至有一個百度百科來詳細解析每一個引數的具體作用,大家感興趣可以移步:
https://baike.baidu.com/item/CreateWindow/5076220
瞭解win32這些模組名稱的意義後,理解上述程式碼的邏輯便很輕鬆了。
圖示載入及工作列圖示顯示配置:
圖示
if icon_path is not None:
# 獲取圖示地址
icon_path = path.realpath(icon_path)
else:
icon_path = resource_filename(Requirement.parse("win10toast"), "win10toast/data/python.ico")
載入格式
icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE
try:
hicon = LoadImage(self.hinst, icon_path, IMAGE_ICON, 0, 0, icon_flags)
except Exception as e:
logging.error("Some trouble with the icon ({}): {}"
.format(icon_path, e))
hicon = LoadIcon(0, IDI_APPLICATION)
工作列圖示
flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip")
Shell_NotifyIcon(NIM_ADD, nid)
Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, "Balloon Tooltip", msg, 200, title, NIIF_ICON_MASK))
等待一會後銷燬
sleep(duration)
DestroyWindow(self.hwnd)
UnregisterClass(self.wc.lpszClassName, None)
這部分程式碼控制了通知彈出框的展示和銷燬。如果你希望通知彈出框久一點再消失,可以適當修改傳入的 duration 變數值。
DestroyWindow後,通知彈出框便消失了,整個 show_toast 的過程結束。
其實非常簡單,從 CreateWindow 到 DestroyWindow 處理彈出框的各種屬性,然後登出窗體,完成整個彈出流程。
3.擴充套件觸發通知
為了擴充套件監聽的按鍵,並能監聽按鍵觸發,需要先了解 notify.py 是如何檢測到按鍵變化的。
獲取按鍵狀態:
keyboard = ctypes.WinDLL("User32.dll")
VK_NUMLOCK = 0x90
VK_CAPITAL = 0x14
def get_capslock_state():
"""Returns the current Caps Lock State(On/Off)"""
return "Caps Lock On" if keyboard.GetKeyState(VK_CAPITAL) else "Caps Lock Off"
def get_numlock_state():
"""Returns The current Num Lock State(On/Off)"""
return "Num Lock On" if keyboard.GetKeyState(VK_NUMLOCK) else "Num Lock Off"
可以看到,獲取按鍵狀態是通過 keyboard.GetKeyState(XXXX) 實現的。
而這個XXXX是對應的按鍵的十六進位制,比如 VK_NUMLOCK 是Num鍵,對應的16進位制程式碼是0x90,VK_CAPITAL 是大小寫按鍵,對應的十六進位制程式碼是0x14.
變數名是可以使用者自定義的,比如大小寫鍵有些人習慣稱之為VK_CAPITAL,也有些人喜歡稱之為 VK_CAPITAL,都可以,只要其最終對應的變數值為十六進位制的0x14即可。
部分按鍵16進位制清單如下(完整版可以閱讀原文檢視):
常數名稱 | 十六進位制值 | 對應按鍵 |
---|---|---|
VK_BACK | 08 | Backspace鍵 |
VK_TAB | 09 | Tab鍵 |
VK_CLEAR | 0C | Clear鍵(Num Lock關閉時的數字鍵盤5) |
VK_RETURN | 0D | Enter鍵 |
VK_SHIFT | 10 | Shift鍵 |
VK_CONTROL | 11 | Ctrl鍵 |
VK_MENU | 12 | Alt鍵 |
VK_PAUSE | 13 | Pause鍵 |
VK_CAPITAL | 14 | Caps Lock鍵 |
再來看看監聽邏輯:
caps_curr = get_capslock_state()
num_curr = get_numlock_state()
while True:
caps_change = get_capslock_state()
num_change = get_numlock_state()
if caps_curr != caps_change:
if caps_change == "Caps Lock On":
pop_up("Caps Lock On", "CapsLock_On.ico")
else:
pop_up("Caps Lock Off", "CapsLock_Off.ico")
caps_curr = caps_change
time.sleep(0.1)
if num_curr != num_change:
if num_change == "Num Lock On":
pop_up("Num Lock On", "NumLock_On.ico")
else:
pop_up("Num Lock Off", "NumLock_Off.ico")
num_curr = num_change
time.sleep(0.2)
在剛開始執行監聽指令碼時,先獲取到按鍵的狀態,在迴圈體中,不斷地獲得當前按鍵狀態,如果發生了狀態變化,則觸發pop_up函式,彈出剛剛我們提到的show_toast 函式:
ef pop_up(body, icon):
"""Generates Pop-up notification when state changes"""
notification = ToastNotifier()
notification.show_toast("Lock Key State", body, icon_path="assets\"+icon, duration=1.5)
整套監聽並通知的機制還是非常簡單的,如果我們想要自定義一些按鍵,你只需要在開頭新增對應的按鍵的十六進位制編碼,然後新增一些監聽函式。
比如我們想監聽 ESC 按鍵被按下:VK_ESCAPE=0x1B,使用 keyboard 模組新增一個鉤子函式,監聽按鍵:
import keyboard as kb
def hook_esc(button):
"""Alert if ESC button is pressed"""
esc_button = kb.KeyboardEvent('down', VK_ESCAPE, 'ESC')
if button.event_type == 'down' and esc_button.name == button.name:
pop_up("ESC Pressed", "CapsLock_On.ico")
# 敲擊後回填為None
button.event_type = None
然後再在迴圈體內新增判斷邏輯:
kb.hook(hook_esc)
效果如下:
當然,圖示和標題還可以進一步優化:
比如將Lock Key State這個標題用 toast_title 變數替代,預設為Lock Key State。這樣在呼叫pop_up函式的時候就能自定義標題了,效果如下:
總而言之,能擴充套件的東西非常多,這只是一個學習的例子,如果大家感興趣的話可以完整原始碼進行改造。
GitHub地址:
https://github.com/skate1512/Toggle_Keys_Notification