Python Ultra Simple Scripting for Windows Key Notification

Posted by altergothen on Tue, 01 Feb 2022 14:35:01 +0100


 

For those who have no backlight on their keyboard, there is no prompt when switching case or controlling the Num key switch. It is often a bad experience to enter some characters tentatively to determine whether the switch is on or off.

As a result, one might think of homemade scripting as a way to notify windows whenever a case or Num switch is triggered:

https://github.com/skate1512/Toggle_Keys_Notification

Let's try this script today. In addition, based on this project, we can extend it to a script that notifies windows whenever a key is triggered or switched:

1. Prepare

 

 

1. Prepare

Select one of the following ways to enter command installation dependencies:
1. The Windows environment opens Cmd (Start-Run-CMD).
2. MacOS environment opens Terminal (command + space input Terminal).
3. If you are using a VSCode editor or Pycharm, you can use Terminal directly below the interface.

pip install win10toast

 

In addition, we need to download the author's code. If you can connect GitHub, go to the following address to download it:
https://github.com/skate1512/Toggle_Keys_Notification

If you can't connect GitHub or the network is slow, reply in the Python Utilities Public Number background: Key Trigger Notification to download the full source code for this article.

2. Source Code Use and Resolution

 

2.1 Source Use

Author's project can be in Toggle_ Keys_ Within the Notification project, run notify.py starts listening:

python notify.py


Click the case switch key once after boot, triggering the notification indicates that the code is working properly:
 

2.2 Source Code Analysis

This project implements pop-up toast notification via win32gui and win32con, the core of which is _ Show_ The toast code is in toast. In py, here is a partial code analysis of this function:

Register and create window s:

message_map = {WM_DESTROY: self.on_destroy, }
# Register Window
self.wc = WNDCLASS()
self.hinst = self.wc.hInstance = GetModuleHandle(None)
self.wc.lpszClassName = str("PythonTaskbar") # Define the name of the window structure
self.wc.lpfnWndProc = message_map
try:
    self.classAtom = RegisterClass(self.wc)
except:
    pass
# Window Format
style = WS_OVERLAPPED | WS_SYSMENU
# Create Window
self.hwnd = CreateWindow(self.classAtom, "Taskbar", style,
                         0, 0, CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         0, 0, self.hinst, None)
UpdateWindow(self.hwnd)

 

The win32 module used is parsed below.

GetModuleHandle: Gets a module handle to an application or dynamic link library.

WM_DESTROY: Close the program.

RegisterClass: Save the defined Window s properties.

WS_OVERLAPPED: Overlapping window with a title bar and border.

WS_SYSMENU: Style with SYSTEM menu bar

CW_USEDEFAULT: Use system default location

CreateWindow has a lot of parameters, even a Baidu Encyclopedia to analyze the specific role of each parameter in detail, you can move on if you are interested:

https://baike.baidu.com/item/CreateWindow/5076220

Once you understand the meaning of the module names win32, it's easy to understand the logic of the above code.

 

Icon loading and taskbar icon display configuration:

# Icon
if icon_path is not None:
    # Get Icon Address
    icon_path = path.realpath(icon_path)
else:
    icon_path = resource_filename(Requirement.parse("win10toast"), "win10toast/data/python.ico")
# Load Format
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)
# Taskbar Icon
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))

# Wait a while and destroy
sleep(duration)
DestroyWindow(self.hwnd)
UnregisterClass(self.wc.lpszClassName, None)

 

This part of the code controls the display and destruction of notification pop-up boxes. If you want to notify the pop-up box to disappear a little longer, you can modify the value of the incoming duration variable appropriately.

After DestroyWindow, the notification pop-up disappears and the entire show_ The toast process ends.

It's really simple, from CreateWindow to DestroyWindow, to process the various properties of a pop-up box, then log off the form and complete the entire pop-up process.

3. Extended Trigger Notification

 

 

In order to extend the keys you listen for and to listen for key triggers, you need to know notify first. How py detects key changes.

Get the key status:

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"

 

You can see that the keyboard is used to get the keyboard status. GetKeyState (XXXX) implementation.

And this XXXX is the hexadecimal of the corresponding key, such as VK_NUMLOCK is the Num key, corresponding to the hexadecimal code 0x90, VK_CAPITAL is a case key with hexadecimal code 0x14.

Variable names are user-customizable, such as case keys, which some people are accustomed to calling VK_CAPITAL, which some people like to call VK_CAPITAL, as long as its final corresponding variable value is hexadecimal 0x14.

The hexadecimal list of some keys is as follows (full version can read the original):

Constant NameHexadecimal ValueCorresponding keys
VK_BACK08Backspace key
VK_TAB09Tab key
VK_CLEAR0CClear key (Num Lock number keyboard 5 when off)
VK_RETURN0DEnter key
VK_SHIFT10Shift key
VK_CONTROL11Ctrl key
VK_MENU12Alt key
VK_PAUSE13Pause key
VK_CAPITAL14Caps Lock key


Let's look at listening logic again:

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)

 

When the listening script is first run, it gets the state of the key first, gets the current state of the key continuously in the loop body, and triggers pop_if the state changes Up function, pop up show_as we just mentioned Toast function:

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)

 

The whole set of mechanisms to listen for and notify is still very simple. If we want to customize some keys, you just need to start by adding the hexadecimal encoding of the corresponding keys and then add some listening functions.

For example, we want to listen for ESC keys being pressed: VK_ESCAPE=0x1B, add a hook function using the keyboard module to listen for keys:

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")
        # Backfill with None after tapping
        button.event_type = None

 

Then add logic to the loop:

kb.hook(hook_esc)

 

The results are as follows:


Of course, icons and titles can be further optimized:

For example, use the title Lock Key State with toast_ Tile variable substitution, defaulting to Lock Key State. This calls pop_ When you use the up function, you can customize the title as follows:

All in all, there's a lot to expand, and this is just an example of learning. If you're interested, you can revert to the programmer's Start Public Notice in the background, press trigger notification, download the full source code for modification.

This is the end of the article. Thank you for watching

To be honest, it's a pleasure to see some of the reader's responses in the background every time. I want to contribute some of my collection of programming dry goods to everyone, to give back to each reader, and I hope to help you.

The main dry goods are:

Over 2000 Python e-books (both mainstream and classic books should be available)

(2) Python Standard Library (most Chinese version)

3. Project Source (Forty or fifty interesting and classic hands-on projects and sources)

(4) Videos about Python basic introduction, crawling, web development, big data analysis (suitable for white learning)

_Summary of all Python knowledge points (you can understand all Python directions and techniques)

* If you can use it, you can take it directly. In my QQ technical exchange group, you can take it by yourself. The group number is 857113825. *

Topics: Python Programming Windows