念电子佛经,积赛博功德 - 使用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())

热门相关:宠宠欲恋   隐婚99天:首长,请矜持   福晋有喜:四爷,宠上天!   我的末世基地车   不负荣光,不负你