extrabacon / python-shell

Run Python scripts from Node.js with simple (but efficient) inter-process communication through stdio
2.08k stars 218 forks source link

how to ship electron app with python? (need help building the program) #254

Closed Khyretos closed 7 months ago

Khyretos commented 2 years ago

The Question:

I am trying to make a usable build for the program I'm making but I cannot make the python scripts run, not with electron-builder nor electron-packager

I want to ship the program with its own python folder so that the user does not have to install python the idea is that my '.venv' (it will later be renamed to 'python' but now its just a proof of concept) folder is shipped with the program image

from what I understand this same directory works when I'm developing: image

but when I try to access the PythonPath property it doesn't show anything

image

this is what I have in my main.js

image

My idea is to make a program that has it all included so that people don't have to install anything additional and everything is self-contained.

I happily accept any suggestion or alternative to make this possible

Khyretos commented 2 years ago

i have been able to make the release run but it only works on my computer because of my local python, how can i make it so the program uses the python that is in the .venv folder

ychinghin commented 2 years ago

If you're using electron-builder, .exe might not work in the asar file, so you need to unpack it when packaging the app.

In package.json, add "build": { "asarUnpack": [ ".venv/**/*" ] } to specify which files to unpack

Reference: https://www.electron.build/configuration/configuration.html

Also check this out: https://github.com/extrabacon/python-shell/issues/152

hockyy commented 7 months ago

oh lol i read the file and rewrite it in appdata path, guess both works

Khyretos commented 7 months ago

Oh, i did not know the issue was still open.

I have been able to solve this by making a .exe and packing it with the installer. thanks for the help.

julian-0 commented 7 months ago

Hi @Khyretos

How did you get python-shell to run your exe file?

I have the same problem you describe, I was able to generate an .exe for the electron application and another one for the python part but I don't know how to put them together.

Khyretos commented 7 months ago

Hi @Khyretos

How did you get python-shell to run your exe file?

I have the same problem you describe, I was able to generate an .exe for the electron application and another one for the python part but I don't know how to put them together.

I dont use python-shell anymore i just spawn it here is the code i use:

const createBackendServer = () =>
  new Promise(resolve => {
    if (main.isPackaged) {
      python = spawn(path.join(pythonPath, './loquendoBot_backend.exe'), [settingsPath, 'prod']);
    } else {
      python = spawn('python', ['-u', path.join(resourcesPath, '../backend/loquendoBot_backend.py'), settingsPath, 'dev']);
    }
    // Capture the stdout of the Python process
    python.stdout.on('data', data => {
      console.info(`${data}`);
    });

    // Capture the stderr of the Python process
    python.stderr.on('data', data => {
      // console.error(`${data}`);
      if (data.toString().startsWith('INFO:waitress:Serving on')) {
        resolve('finished');
      } else {
        console.error(`${data}`);
      }
    });

    // Listen for the Python process to exit
    python.on('close', code => {
      console.log(`Python process exited with code ${code}`);
    });

    if (typeof python.pid !== 'number') {
      console.log('failed');
    } else {
      console.log(`Spawned subprocess correctly!, PID = ${python.pid}`);
    }
  });

i call the 'createBackendServer ' function with this:

async function initiateBackend() {
  try {
    createBackendServer().then(() => {
      getBackendServerStatus();
    });
  } catch (error) {
    console.error('Error during backend initialization:', error);
  }
}

i wrap it up in a promise and spawn it unbuffered ('-u') so i can read all the output. with this i can read the output directly through console. but i do everything with calls.

for further context main.ispackaged is to check if its production or development.

if you need further help just ask.

Khyretos commented 7 months ago

Hi @Khyretos

How did you get python-shell to run your exe file?

I have the same problem you describe, I was able to generate an .exe for the electron application and another one for the python part but I don't know how to put them together.

also this is my file structure:

image

Khyretos commented 7 months ago

Hi @Khyretos

How did you get python-shell to run your exe file?

I have the same problem you describe, I was able to generate an .exe for the electron application and another one for the python part but I don't know how to put them together.

Also just in case (because i ran into this problem). when building make sure to have the installer pack the .exe as well for example i use electron-builder and i have this in my package.json

  "build": {
    "appId": "LoquendoBot",
    "win": {
      "target": [
        "nsis"
      ],
      "icon": "./src/images/icon.ico"
    },
    "nsis": {
      "oneClick": false,
      "installerIcon": "./src/images/icon.ico",
      "uninstallerIcon": "./src/images/icon.ico",
      "uninstallDisplayName": "LoquendoBot-uninstaller",
      "license": "license.md",
      "allowToChangeInstallationDirectory": "true"
    },
    "extraResources": [
      "backend/loquendoBot_backend.exe",

    ]
  },

and to pack everything up i use these scripts:

  "scripts": {
    "start": "electron-forge start",
    "build": "npm run backend && electron-builder",
    "publish": "electron-forge publish",
    "backend": "pyinstaller --noconsole --onefile --collect-all vosk --distpath ./backend ./backend/loquendoBot_backend.py"
  },

'build' creates the python .exe and 'publish' creates the installer. so i combined them in 'build' so it always makes an new executable (in case i made changes) and also makes the installer.

varunagarwal007 commented 1 month ago

@Khyretos were you able to resolve the issue? Cause I need help in developing something similar