百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术流 > 正文

Python 如何快速制作 Windows 按键通知脚本

citgpt 2024-07-20 13:25 8 浏览 0 评论

大家好,我是安果!

对于键盘没有背光灯的同学而言,切换大小写或控制 Num 键开关的时候没有提示,经常需要试探性地输入一些字符来判断开关是否打开,体验非常糟糕

Python 如何快速制作 Windows 按键通知脚本

因此,有人就想到自制脚本这一招,一旦触发大小写切换或 Num 键切换就进行 windows 通知提示:

https://github.com/skate1512/Toggle_Keys_Notification

今天我们来试试这个脚本,此外,我们还可以基于这个项目,扩展成任意一个按键被触发或切换都进行 windows 通知的脚本:


1.准备



请选择以下任一种方式输入命令安装依赖
1. Windows 环境 打开 Cmd (开始-运行-CMD )
2. MacOS 环境 打开 Terminal ( Command + 空格输入Terminal )
3. 如果你用的是 VSCode 编辑器 或 Pycharm,可以直接使用界面下方的Terminal

pip install win10toast


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.hinst = self.wc.hInstance = GetModuleHandle(None)
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 函数:

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)


整套监听并通知的机制还是非常简单的,如果我们想要自定义一些按键,你只需要在开头添加对应的按键的十六进制编码,然后添加一些监听函数。

比如我们想监听 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 函数的时候就能自定义标题了

最后效果图如下:

相关推荐

js中arguments详解

一、简介了解arguments这个对象之前先来认识一下javascript的一些功能:其实Javascript并没有重载函数的功能,但是Arguments对象能够模拟重载。Javascrip中每个函数...

firewall-cmd 常用命令

目录firewalldzone说明firewallzone内容说明firewall-cmd常用参数firewall-cmd常用命令常用命令 回到顶部firewalldzone...

epel-release 是什么

EPEL-release(ExtraPackagesforEnterpriseLinux)是一个软件仓库,它为企业级Linux发行版(如CentOS、RHEL等)提供额外的软件包。以下是关于E...

FullGC详解  什么是 JVM 的 GC
FullGC详解 什么是 JVM 的 GC

前言:背景:一、什么是JVM的GC?JVM(JavaVirtualMachine)。JVM是Java程序的虚拟机,是一种实现Java语言的解...

2024-10-26 08:50 citgpt

使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
跨域(CrossOrigin)

1.介绍  1)跨域问题:跨域问题是在网络中,当一个网络的运行脚本(通常时JavaScript)试图访问另一个网络的资源时,如果这两个网络的端口、协议和域名不一致时就会出现跨域问题。    通俗讲...

微服务架构和分布式架构的区别

1、含义不同微服务架构:微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并...

深入理解与应用CSS clip-path 属性
深入理解与应用CSS clip-path 属性

clip-pathclip-path是什么clip-path 是一个CSS属性,允许开发者创建一个剪切区域,从而决定元素的哪些部分可见,哪些部分会被隐...

2024-10-25 11:51 citgpt

HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
Request.ServerVariables 大全

Request.ServerVariables("Url")返回服务器地址Request.ServerVariables("Path_Info")客户端提供的路...

python操作Kafka

目录一、python操作kafka1.python使用kafka生产者2.python使用kafka消费者3.使用docker中的kafka二、python操作kafka细...

Runtime.getRuntime().exec详解

Runtime.getRuntime().exec详解概述Runtime.getRuntime().exec用于调用外部可执行程序或系统命令,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。...

promise.all详解 promise.all是干什么的
promise.all详解 promise.all是干什么的

promise.all详解promise.all中所有的请求成功了,走.then(),在.then()中能得到一个数组,数组中是每个请求resolve抛出的结果...

2024-10-24 16:21 citgpt

Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解

取消回复欢迎 发表评论: