念电子佛经,积赛博功德 - 使用CircuitPython的HID库实现汉字输入
CircuitPython
CircuitPython说是MicroPython的分支,支持更多芯片,有更多驱动,相对也更复杂。我用它主要是为了MicroPython上面没有的HID,实现键鼠输入。程序上和MicroPython有区别,尤其是GPIO的使用,这也影响了代码的复用。不过总归是Python,移植也不难。标准库的使用可以自行查看官方文档,至于其他库和驱动则要到处找,我也没玩明白。
安装库
我使用的是源地的RP2040开发板,和我的这篇博客中的相同也就是树莓派PICO的芯片。安装好CircuitPython后标准库中自带usb_hid
库。
要实现打字的功能还要安装adafruit_hid
库,文档在这:Adafruit HID Library。
CircuitPython提供了包管理器cirup,确保你的开发板以U盘形式连接到电脑后,使用cirup install adafruit_hid
,即可自动检测开发板型号并安装库。cirup使用期间可能需要挂梯子,也可以从adafruit_hid的仓库clone下来用thonny直接丢进开发板目录。
拼音输入
我的拼音设置的是默认英文模式,按Shift切换汉语拼音。
import time
import board
import digitalio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
kbd = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(kbd)
usrkey = digitalio.DigitalInOut(board.GP24)
usrkey.direction = digitalio.Direction.INPUT
usrkey.pull = digitalio.Pull.UP
def launch_notepad():
kbd.send(Keycode.WINDOWS, Keycode.R)
time.sleep(0.3)
layout.write('notepad\n')
time.sleep(0.5)
while True:
# 按下单片机上的按钮开始输入
if not usrkey.value:
launch_notepad()
kbd.send(Keycode.SHIFT)
layout.write('meinvheguanzaixianfapai')
time.sleep(0.1)
layout.write('1\n')
time.sleep(0.2)
现象:
虽然成功输入了汉字,但不同输入法候选词都有很大不同,就这个中英文切换的设置每个人都可能做自己的设置。这种方法无法保障在每台电脑上正常输入一样的汉字。
Unicode输入
确保输入法在英文模式,不会打开任何候选栏。
我希望有一种通用的输入方式,最好是所有Unicode涵盖的字符通用。bing了一下“windows输入Unicode”,得到了一种方法。在notepad里输入16进制的Unicode,紧跟着按Alt + x
即可转为对应的文字。
今年是龙年,龘对应的Unicode是9f98,在notepad输入
然后按Alt + x
先将准备好的大悲咒保存在开发板目录中
import time
import board
import digitalio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
import board
kbd = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(kbd)
usrkey = digitalio.DigitalInOut(board.GP24)
usrkey.direction = digitalio.Direction.INPUT
usrkey.pull = digitalio.Pull.UP
def launch_notepad():
kbd.send(Keycode.WINDOWS, Keycode.R)
time.sleep(0.3)
layout.write('notepad\n')
time.sleep(0.5)
def typing_unicode(line):
line = line.strip()
for char in line:
# 下面这一套函数看不懂可以自己一个个拆开试试
layout.write(hex(ord(char))[2:])
kbd.send(Keycode.ALT, Keycode.X)
kbd.send(Keycode.ENTER)
while True:
time.sleep(0.2)
if not usrkey.value:
launch_notepad()
with open("dbz.txt", "r") as f:
for line in f:
if not usrkey.value:
break
typing_unicode(line)
支持按键中止输入
现在已经比较满意了,但是在输入较长的文章时,输一半我不想继续了,只能重新插拔。一般想法是把按键绑定中断函数,按下改变输入状态,在输入的时候同时轮询输入状态。
结果一搜傻眼了,CircuitPython没有提供MicroPython那种按键中断,参考这篇文章,要用asyncio来管理按键中断。这个asyncio也要如上文使用cirup安装,不像MicroPython可以直接用。
import time
import board
import digitalio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
import asyncio
import board
import keypad
kbd = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(kbd)
def launch_notepad():
kbd.send(Keycode.WINDOWS, Keycode.R)
time.sleep(0.3)
layout.write('notepad\n')
time.sleep(0.5)
async def typing_unicode(line):
line = line.strip()
for char in line:
layout.write(hex(ord(char))[2:])
kbd.send(Keycode.ALT, Keycode.X)
await asyncio.sleep(0)
kbd.send(Keycode.ENTER)
async def usrkey_handler():
with keypad.Keys((board.GP24,), value_when_pressed=False) as keys:
is_typing = False
loop = asyncio.get_event_loop()
# 这里cancel一次,这样typing_task初始为done的状态
typing_task = asyncio.create_task(typing_csb())
typing_task.cancel()
while True:
event = keys.events.get()
if event:
if event.released:
# 如果没有初始化为done而是none,首次按键会抛异常
if not typing_task.done() and is_typing:
typing_task.cancel()
is_typing = False
else:
typing_task = asyncio.create_task(typing_csb())
is_typing = True
await asyncio.sleep(0)
async def typing_dbz():
launch_notepad()
with open("dbz.txt", "r") as f:
for line in f:
await typing_unicode(line)
asyncio.run(usrkey_handler())