1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Python 制作按鍵觸發(fā)Windows通知的腳本

        共 5589字,需瀏覽 12分鐘

         ·

        2020-12-14 04:33



        對(duì)于鍵盤(pán)沒(méi)有背光燈的同學(xué)而言,切換大小寫(xiě)或控制Num鍵開(kāi)關(guān)的時(shí)候沒(méi)有提示,經(jīng)常需要試探性地輸入一些字符來(lái)判斷開(kāi)關(guān)是否打開(kāi),體驗(yàn)非常糟糕。

        因此,有人就想到自制腳本這一招,一旦觸發(fā)大小寫(xiě)切換或Num鍵切換就進(jìn)行windows通知提示:

        https://github.com/skate1512/Toggle_Keys_Notification

        今天我們來(lái)試試這個(gè)腳本,此外,我們還可以基于這個(gè)項(xiàng)目,擴(kuò)展成任意一個(gè)按鍵被觸發(fā)或切換都進(jìn)行 windows 通知的腳本:

        1.準(zhǔn)備

        開(kāi)始之前,你要確保Python和pip已經(jīng)成功安裝在電腦上,如果沒(méi)有,請(qǐng)?jiān)L問(wèn)這篇文章:超詳細(xì)Python安裝指南?進(jìn)行安裝。如果你用Python的目的是數(shù)據(jù)分析,可以直接安裝Anaconda:Python數(shù)據(jù)分析與挖掘好幫手—Anaconda,它內(nèi)置了Python和pip.

        此外,推薦大家用VSCode編輯器,因?yàn)樗梢栽诰庉嬈飨路降慕K端運(yùn)行命令安裝依賴模塊:Python 編程的最好搭檔—VSCode 詳細(xì)指南。

        Windows環(huán)境下打開(kāi) Cmd (開(kāi)始-運(yùn)行-CMD),蘋(píng)果系統(tǒng)環(huán)境下請(qǐng)打開(kāi) Terminal (command+空格輸入Terminal),輸入命令安裝依賴:

        pip?install?win10toast


        除此之外,我們需要下載作者的代碼,如果你能聯(lián)通GitHub,請(qǐng)前往以下地址下載:
        https://github.com/skate1512/Toggle_Keys_Notification

        如果不能聯(lián)通GitHub,或者網(wǎng)絡(luò)速度比較慢,請(qǐng)?jiān)赑ython實(shí)用寶典公眾號(hào)后臺(tái)回復(fù):按鍵觸發(fā)通知?下載本文完整源代碼。

        2.源碼使用與解析

        2.1 源碼使用

        作者的項(xiàng)目可以在 Toggle_Keys_Notification 項(xiàng)目?jī)?nèi),運(yùn)行 notify.py 啟動(dòng)監(jiān)聽(tīng):

        python?notify.py


        啟動(dòng)后點(diǎn)擊一下大小寫(xiě)切換鍵,觸發(fā)通知?jiǎng)t說(shuō)明代碼正常運(yùn)轉(zhuǎn):


        2.2 源碼分析

        該項(xiàng)目通過(guò)win32gui和win32con實(shí)現(xiàn)了彈出toast進(jìn)行通知的功能,最核心的_show_toast代碼位于 toast.py 中,下面是這個(gè)函數(shù)的部分代碼剖析:

        注冊(cè)和創(chuàng)建 window :

        message_map = {WM_DESTROY: self.on_destroy, }
        # 注冊(cè)Window
        self.wc = WNDCLASS()
        self.hinst = self.wc.hInstance = GetModuleHandle(None)
        self.wc.lpszClassName = str("PythonTaskbar") # 定義該窗口結(jié)構(gòu)的名稱
        self.wc.lpfnWndProc = message_map
        try:
        ????self.classAtom = RegisterClass(self.wc)
        except:
        ????pass
        # Window格式
        style = WS_OVERLAPPED | WS_SYSMENU
        # 創(chuàng)建Window
        self.hwnd = CreateWindow(self.classAtom, "Taskbar", style,
        ?????????????????????????0, 0, CW_USEDEFAULT,
        ?????????????????????????CW_USEDEFAULT,
        ?????????????????????????0, 0, self.hinst, None)
        UpdateWindow(self.hwnd)


        所使用到的win32模塊解析如下。

        GetModuleHandle: 獲取一個(gè)應(yīng)用程序或動(dòng)態(tài)鏈接庫(kù)的模塊句柄。
        WM_DESTROY: 關(guān)閉程序。
        RegisterClass: 將定義好的Window屬性保存保存下來(lái)。
        WS_OVERLAPPED: 重疊式窗口,該式樣窗口 帶有一個(gè)標(biāo)題欄和邊框。
        WS_SYSMENU: 具有 SYSTEM 菜單欄的樣式
        CW_USEDEFAULT: 采用系統(tǒng)默認(rèn)位置

        CreateWindow??這個(gè)函數(shù)具有非常多的參數(shù),甚至有一個(gè)百度百科來(lái)詳細(xì)解析每一個(gè)參數(shù)的具體作用,大家感興趣可以移步:
        https://baike.baidu.com/item/CreateWindow/5076220

        了解win32這些模塊名稱的意義后,理解上述代碼的邏輯便很輕松了。

        圖標(biāo)加載及任務(wù)欄圖標(biāo)顯示配置:

        # 圖標(biāo)
        if?icon_path is not None:
        ????# 獲取圖標(biāo)地址
        ????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)
        # 任務(wù)欄圖標(biāo)
        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))

        # 等待一會(huì)后銷(xiāo)毀
        sleep(duration)
        DestroyWindow(self.hwnd)
        UnregisterClass(self.wc.lpszClassName, None)


        這部分代碼控制了通知彈出框的展示和銷(xiāo)毀。如果你希望通知彈出框久一點(diǎn)再消失,可以適當(dāng)修改傳入的 duration 變量值。

        DestroyWindow后,通知彈出框便消失了,整個(gè) show_toast 的過(guò)程結(jié)束。

        其實(shí)非常簡(jiǎn)單,從 CreateWindow 到 DestroyWindow 處理彈出框的各種屬性,然后注銷(xiāo)窗體,完成整個(gè)彈出流程。

        3.擴(kuò)展觸發(fā)通知

        為了擴(kuò)展監(jiān)聽(tīng)的按鍵,并能監(jiān)聽(tīng)按鍵觸發(fā),需要先了解 notify.py 是如何檢測(cè)到按鍵變化的。

        獲取按鍵狀態(tài):

        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"


        可以看到,獲取按鍵狀態(tài)是通過(guò)?keyboard.GetKeyState(XXXX) 實(shí)現(xiàn)的。

        而這個(gè)XXXX是對(duì)應(yīng)的按鍵的十六進(jìn)制,比如 VK_NUMLOCK 是Num鍵,對(duì)應(yīng)的16進(jìn)制代碼是0x90,VK_CAPITAL 是大小寫(xiě)按鍵,對(duì)應(yīng)的十六進(jìn)制代碼是0x14.

        變量名是可以用戶自定義的,比如大小寫(xiě)鍵有些人習(xí)慣稱之為VK_CAPITAL,也有些人喜歡稱之為?VK_CAPITAL,都可以,只要其最終對(duì)應(yīng)的變量值為十六進(jìn)制的0x14即可。

        部分按鍵16進(jìn)制清單如下(完整版可以閱讀原文查看):

        常數(shù)名稱十六進(jìn)制值對(duì)應(yīng)按鍵
        VK_BACK08Backspace鍵
        VK_TAB09Tab鍵
        VK_CLEAR0CClear鍵(Num Lock關(guān)閉時(shí)的數(shù)字鍵盤(pán)5)
        VK_RETURN0DEnter鍵
        VK_SHIFT10Shift鍵
        VK_CONTROL11Ctrl鍵
        VK_MENU12Alt鍵
        VK_PAUSE13Pause鍵
        VK_CAPITAL14Caps Lock鍵


        再來(lái)看看監(jiān)聽(tīng)邏輯:

        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)


        在剛開(kāi)始運(yùn)行監(jiān)聽(tīng)腳本時(shí),先獲取到按鍵的狀態(tài),在循環(huán)體中,不斷地獲得當(dāng)前按鍵狀態(tài),如果發(fā)生了狀態(tài)變化,則觸發(fā)pop_up函數(shù),彈出剛剛我們提到的show_toast 函數(shù):

        def?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)


        整套監(jiān)聽(tīng)并通知的機(jī)制還是非常簡(jiǎn)單的,如果我們想要自定義一些按鍵,你只需要在開(kāi)頭添加對(duì)應(yīng)的按鍵的十六進(jìn)制編碼,然后添加一些監(jiān)聽(tīng)函數(shù)。

        比如我們想監(jiān)聽(tīng) ESC 按鍵被按下:VK_ESCAPE=0x1B,使用 keyboard 模塊添加一個(gè)鉤子函數(shù),監(jiān)聽(tīng)按鍵:

        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


        然后再在循環(huán)體內(nèi)添加判斷邏輯:

        kb.hook(hook_esc)


        效果如下:


        當(dāng)然,圖標(biāo)和標(biāo)題還可以進(jìn)一步優(yōu)化:

        比如將Lock Key State這個(gè)標(biāo)題用 toast_title 變量替代,默認(rèn)為L(zhǎng)ock Key State。這樣在調(diào)用pop_up函數(shù)的時(shí)候就能自定義標(biāo)題了,效果如下:

        總而言之,能擴(kuò)展的東西非常多,這只是一個(gè)學(xué)習(xí)的例子,如果大家感興趣的話可以在 Python實(shí)用寶典 公眾號(hào)后臺(tái)回復(fù)?按鍵觸發(fā)通知?下載完整源代碼進(jìn)行改造。

        我們的文章到此就結(jié)束啦,如果你喜歡今天的Python 實(shí)戰(zhàn)教程,請(qǐng)持續(xù)關(guān)注Python實(shí)用寶典。

        有任何問(wèn)題,可以在公眾號(hào)后臺(tái)回復(fù):加群,回答相應(yīng)紅字驗(yàn)證信息,進(jìn)入互助群詢問(wèn)。

        原創(chuàng)不易,希望你能在下面點(diǎn)個(gè)贊和在看支持我繼續(xù)創(chuàng)作,謝謝!

        點(diǎn)擊下方閱讀原文可獲得更好的閱讀體驗(yàn)

        Python實(shí)用寶典?(pythondict.com)
        不只是一個(gè)寶典
        歡迎關(guān)注公眾號(hào):Python實(shí)用寶典

        瀏覽 64
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            午夜爱爱爱 | 亚洲国产欧美性爱 | 免费在线观看黄片 | 777午夜精品免费观看狠狠狠 | 韩国r级合集3小14分 | 欧美最猛高潮喷水 | 天天日天天操天天摸 | 程潇被到爽高潮痉挛 | 在线播放a| 超碰免费在线观看 |