You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中如何使用enigo库结合test.json配置实现定时模拟按键操作

问题背景

现有test.json配置文件内容如下:

[
    [1000,800,1],
    [1500,0,2],
    [1600,0,3],
    [2500,300,4]
]

数组每一项的三个元素分别对应:

  • 第一个:触发开始时间,单位毫秒
  • 第二个:按键持续时长,单位毫秒
  • 第三个:按键标识,1/2/3/4分别对应a/b/c/d键

需求:运行时间达到指定开始时间时,按下对应按键并保持指定时长后松开。

现有依赖配置(Cargo.toml)
[dependencies]
delay_timer = "0.10.1"
enigo = "0.0.14"
serde_json = "1.0"
anyhow = "1.0.51"
smol = "1.2.5"
现有main.rs代码
use anyhow::Result;
use delay_timer::prelude::*;
use enigo::*;
use serde_json::{Result as SResult, Value};
use smol::Timer;
use std::fs::File;
use std::io::BufReader;
use std::thread;
use std::time::Duration;

fn main() -> Result<()> {
    let delay_timer = DelayTimerBuilder::default().build();
    let task_instance_chain = delay_timer
        .insert_task(build_task_async_print().unwrap())
        .unwrap();
    let task_instance = task_instance_chain.next_with_wait().unwrap();
    while task_instance.get_state() != delay_timer::prelude::instance::COMPLETED {}
    delay_timer.remove_task(1).unwrap();
    Ok(())
}

fn build_task_async_print() -> Result<Task, TaskError> {
    let mut task_builder = TaskBuilder::default();
    let body = create_async_fn_body!({
        let json_data = get_json().unwrap();
        for element in json_data.as_array().unwrap().iter() {
            Timer::after(Duration::from_millis(element[0].as_f64().unwrap() as u64)).await;
            println!("the value is: {}", element[0]);
            // 此处为需要补全的enigo按键逻辑,当前为固定按a键的示例代码
            let mut enigo = Enigo::new();
            enigo.key_down(Key::Layout('a'));
            thread::sleep(Duration::from_millis(element[1].as_f64().unwrap() as u64));
            enigo.key_up(Key::Layout('a'));
        }
    });
    task_builder.set_task_id(1).spawn(body)
}

fn get_json() -> SResult<Value> {
    let file = File::open("test.json").expect("file should open read only");
    let reader = BufReader::new(file);
    let mut v: Value = serde_json::from_reader(reader)?;
    Ok(v.take())
}
补全实现方案

首先需要在main.rs头部新增导入:use std::time::Instant;

核心修改点如下:

  1. 增加按键标识到字符的映射逻辑,实现动态按键
  2. 修复原有时间计算错误:原逻辑每次循环都等待对应开始时间,会导致后续按键触发时间远晚于预期,改为以任务启动时间为基准计算等待间隔
  3. 将异步块内的阻塞式thread::sleep替换为异步延时,避免阻塞runtime
  4. 将enigo实例初始化移到循环外,减少重复初始化开销

修改后的build_task_async_print函数完整代码:

fn build_task_async_print() -> Result<Task, TaskError> {
    let mut task_builder = TaskBuilder::default();
    let body = create_async_fn_body!({
        let json_data = get_json().unwrap();
        let mut enigo = Enigo::new();
        // 记录任务启动基准时间
        let start_time = Instant::now();

        for element in json_data.as_array().unwrap().iter() {
            let trigger_time = element[0].as_f64().unwrap() as u64;
            let duration = element[1].as_f64().unwrap() as u64;
            let key_id = element[2].as_u64().unwrap() as u8;

            // 计算实际需要等待的时间
            let elapsed = start_time.elapsed().as_millis() as u64;
            if trigger_time > elapsed {
                Timer::after(Duration::from_millis(trigger_time - elapsed)).await;
            }

            // 映射按键
            let key_char = match key_id {
                1 => 'a',
                2 => 'b',
                3 => 'c',
                4 => 'd',
                _ => panic!("不支持的按键标识: {}", key_id)
            };

            println!("触发按键: {},持续时长: {}ms", key_char, duration);
            enigo.key_down(Key::Layout(key_char));
            // 异步等待按键持续时长
            Timer::after(Duration::from_millis(duration)).await;
            enigo.key_up(Key::Layout(key_char));
        }
    });
    task_builder.set_task_id(1).spawn(body)
}

内容的提问来源于stack exchange,提问作者zwl1619

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 19:15:03