mxivideo/docs/python_executor_progress.md

7.6 KiB
Raw Blame History

🚀 通用Python执行器进度支持系统

一个完全重构的通用Python执行器支持实时进度回调、JSON-RPC通信和简化的命令创建。

新特性

  • 🎯 通用进度支持:一次实现,到处使用
  • 🔧 简化的API:多种便利函数和宏
  • 📊 实时进度更新支持Tauri事件和自定义回调
  • 🛠️ 命令构建器简化Python命令构造
  • 📝 丰富的示例:涵盖各种使用场景

功能特性

  • 支持多种Python命令 (python, python3, py)
  • UTF-8编码处理
  • 并发stdout/stderr读取
  • JSON-RPC消息解析
  • 实时进度回调
  • Tauri事件发射
  • 超时处理
  • 错误处理

🎯 核心API

1. 通用进度执行函数

// 最通用的函数 - 推荐使用
execute_python_action_with_progress(
    app: AppHandle,
    module: &str,           // Python模块路径
    action: &str,           // 要执行的动作
    params: &[(&str, &str)], // 参数键值对
    event_name: &str,       // 前端事件名称
    config: Option<PythonExecutorConfig>
) -> Result<String, String>

2. 便利函数

// Tauri事件进度推荐用于前端集成
execute_python_with_events(app, &args, config, "event-name")

// 函数回调进度(用于自定义处理)
execute_python_with_callback(app, &args, config, callback)

// 基本执行(无进度)
execute_python_command(app, &args, config)

3. 宏支持

// 简单命令宏
python_action_command! {
    name: my_simple_command,
    module: "python_core.my_module",
    action: "my_action",
    event: "my-progress-event"
}

📋 使用方法

1. 基本用法(无进度)

use crate::python_executor::execute_python_command;

#[tauri::command]
pub async fn my_command(app: AppHandle) -> Result<String, String> {
    let args = vec![
        "-m".to_string(),
        "my_python_module".to_string(),
        "--action".to_string(),
        "do_something".to_string(),
    ];

    execute_python_command(app, &args, None).await
}

2. 使用Tauri事件进度回调

use crate::python_executor::execute_python_with_events;

#[tauri::command]
pub async fn my_command_with_progress(app: AppHandle) -> Result<String, String> {
    let args = vec![
        "-m".to_string(),
        "my_python_module".to_string(),
        "--action".to_string(),
        "long_running_task".to_string(),
    ];

    // 进度将通过 "my-task-progress" 事件发送到前端
    execute_python_with_events(app, &args, None, "my-task-progress").await
}

3. 使用函数回调

use crate::python_executor::{execute_python_with_callback, PythonProgress};

#[tauri::command]
pub async fn my_command_with_callback(app: AppHandle) -> Result<String, String> {
    let args = vec![
        "-m".to_string(),
        "my_python_module".to_string(),
        "--action".to_string(),
        "process_data".to_string(),
    ];

    // 自定义进度处理
    let progress_callback = |progress: PythonProgress| {
        println!("Progress: {}% - {}", progress.progress, progress.message);
        // 可以在这里添加自定义逻辑,如数据库记录、日志等
    };

    execute_python_with_callback(app, &args, None, progress_callback).await
}

4. 高级用法(自定义回调)

use crate::python_executor::{execute_python_command_with_progress, ProgressCallback, PythonProgress};

struct CustomProgressHandler {
    task_id: String,
}

impl ProgressCallback for CustomProgressHandler {
    fn on_progress(&self, progress: PythonProgress) {
        // 自定义进度处理逻辑
        println!("Task {}: {}% - {}", self.task_id, progress.progress, progress.message);
        
        // 可以发送到数据库、文件、其他服务等
        if progress.step == "complete" {
            println!("Task {} completed!", self.task_id);
        }
    }
}

#[tauri::command]
pub async fn my_advanced_command(app: AppHandle, task_id: String) -> Result<String, String> {
    let args = vec![
        "-m".to_string(),
        "my_python_module".to_string(),
        "--task-id".to_string(),
        task_id.clone(),
    ];

    let progress_handler = CustomProgressHandler { task_id };
    execute_python_command_with_progress(app, &args, None, Some(progress_handler)).await
}

Python端实现

Python脚本需要使用JSON-RPC协议发送进度信息

from python_core.utils.jsonrpc import create_response_handler, create_progress_reporter

def my_long_running_task():
    rpc = create_response_handler("task_id")
    progress = create_progress_reporter()
    
    try:
        # 报告开始
        progress.step("start", "开始处理任务...")
        
        # 处理步骤1
        progress.report("step1", 25.0, "正在处理步骤1...")
        # ... 实际处理逻辑 ...
        
        # 处理步骤2
        progress.report("step2", 50.0, "正在处理步骤2...")
        # ... 实际处理逻辑 ...
        
        # 处理步骤3
        progress.report("step3", 75.0, "正在处理步骤3...")
        # ... 实际处理逻辑 ...
        
        # 完成
        progress.complete("任务完成!")
        
        # 发送最终结果
        result = {
            "status": True,
            "message": "任务成功完成",
            "data": {...}
        }
        rpc.success(result)
        
    except Exception as e:
        progress.error(f"任务失败: {str(e)}")
        rpc.error(-32603, "任务执行失败", str(e))

if __name__ == "__main__":
    my_long_running_task()

前端使用

import { listen } from '@tauri-apps/api/event'

// 监听进度事件
const unlisten = await listen('my-task-progress', (event) => {
  const progress = event.payload as {
    step: string
    progress: number
    message: string
    details?: any
    timestamp: number
  }
  
  console.log(`Progress: ${progress.progress}% - ${progress.message}`)
  
  // 更新UI
  updateProgressBar(progress.progress)
  updateStatusMessage(progress.message)
})

// 执行命令
try {
  const result = await invoke('my_command_with_progress')
  console.log('Task completed:', result)
} finally {
  unlisten() // 清理监听器
}

进度数据结构

pub struct PythonProgress {
    pub step: String,           // 当前步骤名称
    pub progress: f64,          // 进度百分比 (0-100, -1表示不确定)
    pub message: String,        // 进度消息
    pub details: Option<serde_json::Value>, // 额外详情
    pub timestamp: f64,         // 时间戳
}

配置选项

pub struct PythonExecutorConfig {
    pub timeout_seconds: u64,                    // 超时时间(秒)
    pub working_directory: Option<PathBuf>,      // 工作目录
}

// 默认配置10分钟超时当前目录
let config = PythonExecutorConfig::default();

// 自定义配置
let config = PythonExecutorConfig {
    timeout_seconds: 1800, // 30分钟
    working_directory: Some("/path/to/workdir".into()),
};

最佳实践

  1. 选择合适的回调方式

    • 简单任务:使用 execute_python_command
    • 需要前端进度显示:使用 execute_python_with_events
    • 需要自定义处理:使用 execute_python_with_callback
  2. Python端进度报告

    • 使用有意义的步骤名称
    • 提供清晰的进度消息
    • 合理设置进度百分比
    • 在关键节点报告进度
  3. 错误处理

    • Python端使用try-catch包装
    • 通过JSON-RPC发送错误信息
    • Rust端处理超时和进程错误
  4. 性能考虑

    • 不要过于频繁地报告进度
    • 避免在进度回调中执行耗时操作
    • 合理设置超时时间