【问题标题】:How to correctly store user input (keyboard,mouse) in file using keyboard & mouse Python modules?如何使用键盘和鼠标 Python 模块将用户输入(键盘、鼠标)正确存储在文件中?
【发布时间】:2020-01-03 12:30:35
【问题描述】:

我正在使用键盘和鼠标模块来记录用户与我们软件的交互,以便我们可以进行一些高级 GUI 测试。 目前我正在尝试将记录的事件存储在文本文件中,然后再次播放此记录。

但是,当我从所述文件加载记录的事件时,我只看到播放的 mouse 事件和 no keyboard 事件。

这个问题的一个原因可能是键盘事件的实现。 KeyboardEvents 不包含__repr__ 的正确实现。这会阻止我们调用print(keyboard_events, file=f) 并读取带有eval(line) 的行。 (这适用于鼠标和 ButtonEvent 和 MoveEvent) 所以我们决定使用 KeyboardEvents 的 json 格式。基本上我们正在做的是我们检索每个 KeyboardEvent 的 json 格式并将 json 写入文件中。然后我们加载 json 文件并将 json 解析为 KeyboardEvents。

目前我们将鼠标和键盘输入存储在一个文件中。然而,由于mouse 支持__repr__ 的正确实现,我们可以直接打印鼠标事件并在其上使用eval() 来检索存储的事件。

这是用于录制和播放的文件:

import threading
import mouse
import keyboard

from mouse import ButtonEvent
from mouse import MoveEvent
from mouse import WheelEvent

from keyboard import KeyboardEvent

import time

import json
import sys

def record(file='record.txt'):
    f = open(file, 'w+')
    mouse_events = []
    keyboard_events = []
    keyboard.start_recording()
    starttime = time.time()
    mouse.hook(mouse_events.append)
    keyboard.wait('esc')
    keyboard_events = keyboard.stop_recording()
    mouse.unhook(mouse_events.append)
    #first line = start of recording
    #mouse events = second line
    #keyboard events = every remaining line = 1 event
    print(starttime, file=f)
    print(mouse_events, file=f)
    for kevent in range(0, len(keyboard_events)):
        print(keyboard_events[kevent].to_json(), file = f)
    f.close()

def play(file, speed = 0.5):
    f = open(file, 'r')
    #per definition the first line is mouse events and the rest is keyboard events
    lines = f.readlines()
    f.close()
    mouse_events = eval(lines[1])
    keyboard_events = []
    for index in range(2,len(lines)):
        keyboard_events.append(keyboard.KeyboardEvent(**json.loads(lines[index])))

    starttime = float(lines[0])
    keyboard_time_interval = keyboard_events[0].time - starttime
    keyboard_time_interval /= speed
    mouse_time_interval = mouse_events[0].time - starttime
    mouse_time_interval /= speed
    print(keyboard_time_interval)
    print(mouse_time_interval)
    #Keyboard threadings:
    k_thread = threading.Thread(target = lambda : time.sleep(keyboard_time_interval) == keyboard.play(keyboard_events, speed_factor=speed) )
    #Mouse threadings:
    m_thread = threading.Thread(target = lambda : time.sleep(mouse_time_interval) == mouse.play(mouse_events, speed_factor=speed))
    #start threads
    m_thread.start()
    k_thread.start()
    #waiting for both threadings to be completed
    k_thread.join() 
    m_thread.join()


if __name__ == '__main__':
    if len(sys.argv) > 2 and sys.argv[1] == 'play':
        play(sys.argv[2])
    elif len(sys.argv) >= 2 and sys.argv[1] == 'record':
        if(len(sys.argv)) == 3:
            record(sys.argv[2])
        else:
            record()
    else:
        print("missing either 'play' or 'record' or filename")

我希望此代码的行为与在单个函数中运行时一样(请参阅https://stackoverflow.com/a/57670484/7345513 中的编辑)。

含义:我希望线程中的播放能够同步并且按键被按下。我实际上得到的是鼠标事件按需要播放,但没有处理键盘事件。当我使用链接 SO 中的函数时,它可以工作。

有人可以指点我正确的方向吗?

【问题讨论】:

  • 您不能依赖repr(arbitrary_object) 提供可以用eval() 反转的表示。因此,只要它返回一个字符串,它就不是一个不正确的实现。第三方可以以__repr__() 的形式返回任何他们想要的东西,所以你甚至不能依赖mouse 在未来总是给你你想要的东西。 repr() 是一个调试工具,不适合业务逻辑。最好依赖更健壮的序列化方法,例如 JSON、XML、YAML、pickle 或任何您喜欢的方法。
  • keyboard 已经实现了 KeyboardEvent.to_json(),据我所知:github.com/boppreh/… 因此,如果您想要健壮性,这可能是最简单的路径。
  • 我明白了。但我已经在使用 to_json() 功能了。我的问题是,当我将 json 字符串加载回 python 时,不会调用 KeyboardEvents。也许我领导/写错了?我所拥有的一切都在粘贴的源代码中......
  • 好的,我已经重试了我的代码,基本上尝试在单个 python 会话中调试它(通常此代码将通过调用 python record.py record 启动),现在两个输入都已加载。然而keyboard 的时间似乎已经关闭。我认为这是因为它从第一个输入开始。这可以解决......而且我现在看到并非所有输入都转发到其他应用程序。这可能是一个更大的问题(ctrl+N 即)
  • 时间不是你期望的方式(即键盘输出开始太早)的原因是record()存储了绝对时间戳(unix时间戳)。因此,play() 播放事件的唯一方法是立即播放第一个事件,这不是您想要的。要解决此问题,您还必须保留开始录制的时间戳,然后在您想要回放事件时计算 first_interval = keyboard_events[0].time - timestamp_when_recording_started 并将结果用于 sleep(first_interval),然后再调用 play()

标签: python multithreading keyboard mouse


【解决方案1】:

我忘了回答我自己的问题,但它是这样的:

感谢 blubberdiblub 的指导,我已经跟踪了跟踪的开始时间,然后可以在第一次输入发生时添加一个偏移量。这使得回放的时间可以稍微准确一些。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多