Eel icon indicating copy to clipboard operation
Eel copied to clipboard

Using Electron as front end

Open ChrisKnott opened this issue 7 years ago • 47 comments

I mainly use Eel to make prototypes or personal/internal tools, where polish is not that important, but if you want to make a more professional looking app with HTML/JS frontend, and Python backend, you can use Electron as the browser. This will give you total control over things like right click menus, menubars etc.

I have added a new custom mode which just runs whatever command you provide in args. In the future I might add more "first class" support for Electron.

The absolute smallest possible Electron app needs two files, a package.json file that looks like...

{
  "main": "main.js",
  "devDependencies": {
    "electron": "^2.0.0"
  }
}

and a main.js that looks something like...

const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow

let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({width: 800, height: 600})
  mainWindow.loadURL('http://localhost:8000/start_page.html');
  mainWindow.on('closed', function () {
    mainWindow = null
  })
}

app.on('ready', createWindow)

app.on('window-all-closed', function () {
  app.quit()
});

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

Then, in your python script, you need to specify options something like...

options = {
	'mode': 'custom',
	'args': ['/usr/local/bin/electron', '.'],
	(...)
}
eel.init('web')
eel.start('start_page.html', size=(800, 600), options=options)

ChrisKnott avatar Jun 20 '18 19:06 ChrisKnott

Thanks for this work - I'll check it out.

My recent software product is now complete. It definately needed menus, a standalone build of chrome and other electron features so I had no choice but to jump ship and use https://github.com/fyears/electron-python-example. But I'll re-check out the feasibility of eel with electron for my next project.

abulka avatar Jun 21 '18 00:06 abulka

@ChrisKnott So I went ahead and created my own electron app (without eel). Then, I put gui.py in that folder:

import eel

options = {
	'mode': 'custom',
	'args': ['C:/Users/x/PycharmProjects/adidas/gui']
}

eel.init('html')
eel.start('index.html', options=options)

Here is my main.js:

const {app, BrowserWindow} = require('electron')

let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({width: 1206, height: 729, icon:'images/favicon-32x32.png', resizable: false, title: ""})
  mainWindow.setMenu(null);
  mainWindow.loadFile('http://localhost:8000/index.html')

  mainWindow.on('closed', function () {
    mainWindow = null
  })
}
app.on('ready', createWindow)

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

The Electron app works fine when I run it via npm run. However, when I try and run gui.py I get the following error:

Traceback (most recent call last): File "C:/Users/x/PycharmProjects/adidas/gui.py", line 9, in eel.start('index.html', options=options) File "C:\Users\x\AppData\Local\Programs\Python\Python36-32\lib\site-packages\eel_init_.py", line 112, in start brw.open(start_urls, options) File "C:\Users\x\AppData\Local\Programs\Python\Python36-32\lib\site-packages\eel\browsers.py", line 39, in open stdout=sps.PIPE, stderr=sps.PIPE, stdin=sps.PIPE) File "C:\Users\x\AppData\Local\Programs\Python\Python36-32\lib\site-packages\gevent\subprocess.py", line 604, in init restore_signals, start_new_session) File "C:\Users\x\AppData\Local\Programs\Python\Python36-32\lib\site-packages\gevent\subprocess.py", line 955, in _execute_child startupinfo) PermissionError: [WinError 5] Access is denied

This is my file structure within the gui folder:

css

global.css

html

index.html

node_modules gui.py main.js package.json package-lock.json renderer.js

Any ideas?

04fsnape avatar Jun 21 '18 12:06 04fsnape

args should point to the electron executable. When you run npm start, what does it say in the command prompt as it starts up? Should be something like electron.exe .

ChrisKnott avatar Jun 21 '18 13:06 ChrisKnott

@ChrisKnott OK so I found the electron exe file.

Now my gui.py file looks like this:

import eel

options = {
	'mode': 'custom',
	'args': ['C:/Users/x/PycharmProjects/adidas/gui/node_modules/electron/dist/electron.exe', '.']
}

eel.init('html')
eel.start('index.html', options=options)

When I run this, it opens an electron browser but it is just blank. When I go to localhost:8000 on my browser, I can see my page but there is no CSS being applied to it...

What am I missing here?

EDIT: When I view source on localhost:8000/index.html and click on the link to my css file (../css/global.css), I get a 404 error. Why is this not showing up?

EDIT2: Changed loadFile to loadURL in main.js. Now it opens the page but again without styling. How can I add all of my folders to localhost?

04fsnape avatar Jun 21 '18 21:06 04fsnape

Also, closing the app doesn't end the python script.

04fsnape avatar Jun 21 '18 21:06 04fsnape

Fixed. I put the html, css, js and images folders in a folder called web, then I did eel.init('web) and eel.start('html/index.html').

Works great now except for the issue with the python script still running.

04fsnape avatar Jun 21 '18 21:06 04fsnape

To close the Python script just call into it from Electron (in the close handler) then call sys.exit() on the Python side

ChrisKnott avatar Jun 22 '18 08:06 ChrisKnott

@ChrisKnott I'll test that in a bit bit right now I'm having another problem. I added a custom title bar and close/minimise buttons which works absolutely fine when I run it via npm start. However, when I run it via eel it shows an old version of my project and nothing will update. I even tried reverting it back in case my title bar code was causing an issue and just changing one letter of an element, but it still shows the old version and nothing will change. I tried restarting my PC but the issue persists.

What's going on here?

04fsnape avatar Jun 22 '18 10:06 04fsnape

Hmmm not sure really, perhaps you have two copies of the file?

ChrisKnott avatar Jun 22 '18 10:06 ChrisKnott

@ChrisKnott I tried it a few hours after and it just worked... Strange.

Also I got the closing to work properly, thanks.

I even made a custom title bar in electron which looks great.

04fsnape avatar Jun 22 '18 16:06 04fsnape

It seems like the issue was caused by electron's caching which can be cleared by going to C:\Users<user>\AppData\Roaming<project>\Cache and deleting the files.

04fsnape avatar Jun 22 '18 17:06 04fsnape

@ChrisKnott Just wanted to say thanks for adding this capability. It's working really well for me!

nealdav avatar Jul 18 '18 04:07 nealdav

Thanks Neal I appreciate it

ChrisKnott avatar Jul 18 '18 14:07 ChrisKnott

@ChrisKnott Hi Chris, thanks so much for this.

1 question - I'm trying to the above suggestion with Electron, however whenever I run my .py script, instead of launching a new window in Electron, it just adds a new tab to my chrome browser. Otherwise, it just opens a new instance of Chrome.

Seems like it's ignoring the fact that I want to launch it using Electron and uses Chrome instead.

nba94 avatar Aug 12 '18 13:08 nba94

@nba94 If it's opening a Chrome tab, then eel must be opening a new instance of Chrome instead of Electron. Do you have 'mode': 'custom' added to the options dictionary?

ChrisKnott avatar Aug 14 '18 18:08 ChrisKnott

@ChrisKnott I do. I have the following as options:

options1 = { 'mode': 'custom', 'port': 8000, 'args': ['/usr/local/bin/electron','.'], }

and this at the end: eel.start('index.html',options=options1)

Yet it still launches Chrome for some reason.

nba94 avatar Aug 15 '18 10:08 nba94

Maybe you are still running the old version of eel...?

ChrisKnott avatar Aug 15 '18 10:08 ChrisKnott

@ChrisKnott Yes.. Now it recognizes it! Thanks and sorry...

nba94 avatar Aug 15 '18 11:08 nba94

@ChrisKnott Thanks for your help so far - this is an amazing addition to the module.

I have a question.. When I tried to compile the application using pyinstaller, what is the correct way to include Electron?

Do you include it in datas as a complete .app executable file to be stored within the .app itself? I have tried doing that, but that did not work.

Maybe there is something that I am missing out?

FYI I am on MacOS.

nba94 avatar Sep 09 '18 19:09 nba94

I think the best way is to use --add-data option to include Electron binaries. There is more info on pyinstaller's help https://media.readthedocs.org/pdf/pyinstaller/cross-compiling/pyinstaller.pdf

These will be extracted to a temp folder when the .app is run. You want to use the path of that temp folder in your args list

ChrisKnott avatar Sep 30 '18 16:09 ChrisKnott

@ChrisKnott I finally worked it out..

Just in case anyone else will be wondering these are the following steps I took to compile into a single file on MacOS:

  1. Download compiled electron from https://github.com/electron/electron/releases (for me it was electron-v2.0.11-darwin-x64.zip)

  2. Right click -> open contents of the downloaded Electron, create 'app' folder in the Contents -> Resources and move my main.js, index.html and package.json inside.

  3. As Chris mentioned above, --add-data has to be used to include Electron, however for some reason it does not include some of the libraries for Electron to run successfully when compiled, so including Electron and those files as follows:

  • --add-data [include Electron.app that you have downloaded previously]

  • --add-data /Users/[path to downloaded Electron]/Electron.app/Contents/Frameworks/Electron\ Framework.framework/Versions/A:Electron.app/Contents/Frameworks/Electron\ Framework.framework

  • (optional) instead of point 2 it is also possible to just write --add-data for those files required to include inside Electron, following this format: --add-data /Users/[path to your main.js]/main.js:Electron.app/Contents/Resources/app

After making sure these files are included everything ran as expected.

FYI In case anyone is planning to compile using --onefile - on MacOS you have to make sure all of the references to any paths in your script are using relative paths. The following function ensures that the path to any file you use in your script that is saved locally is turned into a relative path:

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

Even options has to be rewritten using the function in order for --onefile compilation to work correctly, as follows:

options1 = {
     'mode': 'custom',
     'port': 8000,
     'args': [resource_path('Electron.app/Contents/MacOS/Electron'),'.'],
 }

Sorry for the lengthy post, but maybe this will help someone spend way less time than I have on this!

nba94 avatar Oct 01 '18 21:10 nba94

@nba94 Thanks for the detailed writeup, I'm sure this will help lots of people

ChrisKnott avatar Oct 01 '18 21:10 ChrisKnott

anyone can help on how to add eel.js while using electron ?

greixs avatar Apr 20 '19 10:04 greixs

You now longer have to use 'custom' mode for this, you can say 'electron' in latest version (currently in beta)

You may also want to use;

import eel.browsers
eel.browsers.set_path('electron', 'node_modules/electron/dist/electron')

to force using binary from local folder (this should also help with packaging with pyinstaller).

I have added an example for a minimal electron app. I will improve it when beta is released fully on PyPI.

For now I am going to close this issue as there are too many open atm.

ChrisKnott avatar Jun 14 '19 11:06 ChrisKnott

To close the Python script just call into it from Electron (in the close handler) then call sys.exit() on the Python side

Close handler is not in render process ,how to call into python script ? I'm confused

zphiliam avatar Jul 23 '19 08:07 zphiliam

Yes I think it is not as easy as it seems, I will make an example with clean shutdown when I have time, hopefully in w/c 12th August as I have time off work then

ChrisKnott avatar Jul 31 '19 19:07 ChrisKnott

Yes I think it is not as easy as it seems, I will make an example with clean shutdown when I have time, hopefully in w/c 12th August as I have time off work then

Any updates to this?

w6tsang avatar Dec 03 '19 16:12 w6tsang

Hi @w6tsang - I've taken over primary maintenance of this project for now, and unfortunately I'm not super familiar with this side of the project. I'll try to look into it when I have time - but in the mean time if anyone else comes across this and has more experience and the willingness to share ways that might work, it would be really appreciated.

samuelhwilliams avatar Dec 07 '19 13:12 samuelhwilliams

I have a project using Electron + Eel 1.0 so I will probably submit fix for this at some point

ChrisKnott avatar Dec 07 '19 15:12 ChrisKnott

Any Updates on Electron + Eel ? Please include the integration of Electron with Eel in the Documentation

iomkarbhad avatar May 08 '20 07:05 iomkarbhad