Electron 执行python脚本

1 需求背景

有个需求需要Electron执行在本地执行python脚本。希望通过Electron调用python服务并且实现双向通信。

2 解决思路

使用Electon 的{ exec, spawn, execFile, fork } from "child_process"; 能力来执行python脚本,使用spawn可以实现持续交互,稍后见示例代码。

2.1 在electon打包python和python文件

在开发环境,可以通过直接使用已有python和python文件来测试electron能否执行python。

js 复制代码
// 完成代码后后文附上,这里知道在执行python文件就行了
const pythonProcess = spawn(pythonPath, [scriptPath]);

结论:可以执行

2.2 在生产环境测试python执行

在生产环境python包和python文件需要放到resources,打包完成之后,你可以在resources文件夹里面看到python文件夹和你的python文件。

打包配置如下:

json 复制代码
"extraResources": [
    {
      "from": "python_env.zip",
      "to": "python_env.zip",
      "filter": [
        "**/*"
      ]
    },
    {
      "from": "electron/main/python",
      "to": "python_scripts",
      "filter": [
       "**/*.py"
      ]
    }
  ]

打包后的结果:

2.3 使用python第三方sdk

在实际应用中肯定不能只用python包,也许使用python sdk。继续调研后得到方法,可以直接使用 python 虚拟环境,python虚拟环境是一个包含你所有三方sdk的独立环境,方便移植。

numpy行测试,输出符合预期。

创建python虚拟环境步骤如下

复制代码
# 创建虚拟开发环境
`python3 -m venv python_env`

# 激活虚拟环境
`source python_env/bin/activate`

# 生成 requirement.txt

`pip3 freeze > requirements.txt`

# 安装依赖

`pip3 install -r requirements.txt`

把虚拟环境文件夹python_env打包成zip放到Electron项目里。
注意!!!

需要使用压缩包,在Electron里main直接使用python_env文件夹,可能会打包失败。

2.4 解压缩python虚拟环境,运行python脚本

最近比较忙,到这一步搁置了。以后补上。

示例代码(干货)

ececPy.ts

js 复制代码
import { exec, spawn, execFile, fork } from "child_process";
import path from "node:path";
import fs from "fs";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const devPythonPath = path.join(__dirname, "../..");

// 查找 Python 可执行文件
function findPython() {
  console.log("devPythonPath:", devPythonPath);
  const possibilities1 = [
    // 在打包的应用中
    path.join(process.resourcesPath, "python_env", "bin", "python3.9"),
  ];

  for (const pythonPath of possibilities1) {
    if (fs.existsSync(pythonPath)) {
      return pythonPath;
    }
  }
  console.log("Could not find python3 for product, checked", possibilities1);
  const possibilities2 = [
    // 在开发环境中
    path.join(devPythonPath, "python_env", "bin", "python3.9"),
  ];
  for (const pythonPath of possibilities2) {
    if (fs.existsSync(pythonPath)) {
      return pythonPath;
    }
  }
  console.log("Could not find python3 for dev, checked", possibilities2);
  console.log("测试环境请吧python压缩包解压到项目根目录");
  const possibilities3 = [
    // 如果上述路径找不到,尝试系统默认的 python3
    "python3",
  ];
  for (const pythonPath of possibilities2) {
    if (fs.existsSync(pythonPath)) {
      return pythonPath;
    }
  }
  console.log("Could not find python3 for dev, checked", possibilities3);
  return null;
}

// 启动 Python 进程并进行交互
export async function startPingPong() {
  console.log("call start pingpong");
  const pythonPath = findPython();
  if (!pythonPath) {
    console.error("Python not found");
    return;
  }

  // 使用 spawn 而不是 execFile 以便进行持续交互
  // 生产环境路径
  let scriptPath = path.join(
    process.resourcesPath,
    "/python_scripts/pingpong.py"
  );
  console.log("生产环境 scriptPath:", scriptPath);
  if (!fs.existsSync(scriptPath)) {
    scriptPath = "";
  }

  // 测试环境路径
  if (!scriptPath) {
    scriptPath = path.join(devPythonPath, "/electron/main/python/pingpong.py");
    console.log("测试环境 scriptPath:", scriptPath);
  }

  const pythonProcess = spawn(pythonPath, [scriptPath]);

  // 处理 Python 输出
  pythonProcess.stdout.on("data", (data: any) => {
    try {
      const response = JSON.parse(data.toString());
      console.log("Received from Python:", response);

      // 如果收到 pong,继续发送 ping
      if (response.action === "pong") {
        setTimeout(() => {
          sendPing(pythonProcess, response.count);
        }, 1000);
      }
    } catch (error) {
      console.error("Error parsing Python response:", error);
    }
  });

  // 处理错误
  pythonProcess.stderr.on("data", (data: any) => {
    console.error("Python error:", data.toString());
  });

  // 进程退出
  pythonProcess.on("close", (code: any) => {
    console.log(`Python process exited with code ${code}`);
  });

  // 发送初始 ping
  sendPing(pythonProcess, 0);
}

// 发送 ping 到 Python
function sendPing(process: any, count: any) {
  const message = {
    action: "ping",
    count: count,
    timestamp: Date.now(),
  };

  console.log("Sending to Python:", message);
  process.stdin.write(JSON.stringify(message) + "\n");
}

export const unzipPython = () => {
  // TODO: 解压python压缩包
};

pingpong.py

python 复制代码
import sys
import json
import time
import numpy as np
arr1 = np.array([1, 3, 2, 5, 4])
arr1_str = json.dumps(arr1.tolist())

def main():
# 简单的 ping-pong 交互
    for line in sys.stdin:
        try:
            # 解析从 Node.js 发送来的数据
            data = json.loads(line.strip())
            
            if data.get('action') == 'ping':
                # 收到 ping,回复 pong
                response = {
                    'action': 'pong',
                    'timestamp': time.time(),
                    'count': data.get('count', 0) + 1,
                    'arr1_str': arr1_str
                }
                print(json.dumps(response))
                sys.stdout.flush()  # 确保立即发送响应
                
            elif data.get('action') == 'exit':
                # 退出命令
                break
                
        except json.JSONDecodeError:
            # 处理无效的 JSON 数据
            error_response = {
                'error': 'Invalid JSON',
                'received': line.strip()
            }
            print(json.dumps(error_response))
            sys.stdout.flush()

if __name__ == '__main__':
    main()
相关推荐
傻啦嘿哟24 分钟前
Python SQLite模块:轻量级数据库的实战指南
数据库·python·sqlite
Q_Q51100828532 分钟前
python+django/flask+uniapp基于微信小程序的瑜伽体验课预约系统
spring boot·python·django·flask·uni-app·node.js·php
XueminXu36 分钟前
Python读取MongoDB的JSON字典和列表对象转为字符串
python·mongodb·json·pymongo·mongoclient·isinstance·json.dumps
techdashen44 分钟前
12分钟讲解Python核心理念
开发语言·python
jie*1 小时前
小杰机器学习(nine)——支持向量机
人工智能·python·机器学习·支持向量机·回归·聚类·sklearn
闭着眼睛学算法1 小时前
【华为OD机考正在更新】2025年双机位A卷真题【完全原创题解 | 详细考点分类 | 不断更新题目 | 六种主流语言Py+Java+Cpp+C+Js+Go】
java·c语言·javascript·c++·python·算法·华为od
郝学胜-神的一滴1 小时前
谨慎地迭代函数所收到的参数 (Effective Python 第31条)
开发语言·python·程序人生·软件工程
烛阴1 小时前
【TS 设计模式完全指南】构建你的专属“通知中心”:深入观察者模式
javascript·设计模式·typescript
lumi.1 小时前
Vue.js 从入门到实践1:环境搭建、数据绑定与条件渲染
前端·javascript·vue.js
二十雨辰1 小时前
vue核心原理实现
前端·javascript·vue.js