0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pythonでkey入力テスト

Posted at

目的

pythonでkey入力のテストを行う。
seleniumでのwebへの自動入力でテストを行う方法は良く使われます。
ただ、もっと単純にキー入力のエミュレートだけしたい場合もあると思います。
pythonのライブラリでkeyboardというものがありましたので、使ってみたいと思います。


keyboardライブラリについて

keyboardはキーボード入力のエミュレート等ができます。
今回の目的に必要なのは、以下の関数となります。
keyboard.press_and_releasekeyboard.send

とりあえず、複雑なことは考えずにキーコードで送るだけを考えます。


各機能の実装

入力データ

スクリプトや文字列で入力データを指定できるように考えます。
また、wait時間も欲しいため、文字列中に埋め込めるようにします。

入力仕様概要

入力文字列の仕様は以下のような感じにします。

  1. @wait(<num>)
    <num> [sec] 待機する

  2. "<string>"
    任意の文字列(<string>)を送信する
    エスケープ文字列に対応する

  3. 空白や改行はスキップする


key送信処理について

キーコード送信には、keyboard.press_and_release を使用します。、
エスケープシーケンス文字列をコードに戻すために、codecs.decodeを使用します。

    def send_keys(self, keys: str):
        """Send keys to emulate keyboard actions."""
        for key in keys:
            keyboard.press_and_release(key)

    def send_escape_text(self, text: str):
        """Send escape text to emulate keyboard actions."""
        keys = codecs.decode(text, 'unicode-escape')
        self.send_keys(keys)


入力データ解析

今回はお手軽に正規表現で対応します。

    def __init__(self):
        """Initialize EmuKeyboard class."""
        self.pattern = r'(@wait\((?P<wait>\d+(\.\d+)?)\)|"(?P<string>[^"]*)"|(?P<other>\S+))'
        self.text = ""

    def analyze(self, text: str):
        """Analyze the given text and yield key actions."""
        matches = re.finditer(self.pattern, text)
        for match in matches:
            groups = match.groupdict()
            if groups['wait']:
                yield ('wait', float(groups['wait']))
            elif groups['string']:
                yield ('string', groups['string'])
            elif groups['other']:
                yield ('string', groups['other'])

analyze関数に文字列を渡すと、正規表現を使って分類して返します。


全体

コマンドライン引数を受け取る処理も追加して、全体を作成します。

emukeyboard.py
import keyboard
import re
import time
import codecs
import argparse
import sys

class EmuKeyboard:
    def __init__(self):
        """Initialize EmuKeyboard class."""
        self.pattern = r'(@wait\((?P<wait>\d+(\.\d+)?)\)|"(?P<string>[^"]*)"|(?P<other>\S+))'
        self.text = ""

    def wait(self, sec: float = 1.0):
        """Wait for the specified seconds."""
        time.sleep(sec)

    def send_keys(self, keys: str):
        """Send keys to emulate keyboard actions."""
        for key in keys:
            keyboard.press_and_release(key)

    def send_escape_text(self, text: str):
        """Send escape text to emulate keyboard actions."""
        keys = codecs.decode(text, 'unicode-escape')
        self.send_keys(keys)

    def load(self, script: str):
        """Load script file and return its content."""
        try:
            with open(script, 'r', encoding='utf-8') as f:
                self.text = f.read()
                return self.text
        except FileNotFoundError:
            print(f"File not found: {script}")
            return None

    def send_text(self, text: str):
        """Send text to emulate keyboard actions."""
        self.text = text
        self.run()

    def run(self):
        """Run the script to emulate keyboard actions."""
        for keypair in self.analyze(self.text):
            if keypair[0] == 'wait':
                self.wait(keypair[1])
            else:
                self.send_escape_text(keypair[1])

    def analyze(self, text: str):
        """Analyze the given text and yield key actions."""
        matches = re.finditer(self.pattern, text)
        for match in matches:
            groups = match.groupdict()
            if groups['wait']:
                yield ('wait', float(groups['wait']))
            elif groups['string']:
                yield ('string', groups['string'])
            elif groups['other']:
                yield ('string', groups['other'])
            else:
                pass

def argparser() -> argparse.Namespace:
    """Parse command-line arguments and return them."""
    parser = argparse.ArgumentParser(description='Key Emulator')

    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-k', '--keys', type=str, default=None, help='Keys to be emulated')
    group.add_argument('-s', '--script', type=str, default=None, help='Script file containing keys to be emulated')

    parser.add_argument('-w', '--wait', type=float, default=1.0, help='Wait time before sending the keys')

    try:
        return parser.parse_args()
    except argparse.ArgumentError as e:
        print(f"Error: {str(e)}")
        sys.exit(1)
    except SystemExit:
        parser.print_help()
        sys.exit(2)

def main() -> None:
    """Main function to parse arguments and execute EmuKeyboard."""
    args = argparser()
    ek = EmuKeyboard()
    ek.wait(args.wait)
    if args.script is not None:
        if (ek.load(args.script) is not None):
            ek.run()
    else:
        ek.send_text(args.keys)

if __name__ == '__main__':
    main()

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?