Eel: Using Electron as front end

Created on 20 Jun 2018  路  34Comments  路  Source: ChrisKnott/Eel

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)

Most helpful comment

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

All 34 comments

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.

@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?

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 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?

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

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.

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

@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?

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

@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.

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

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

Thanks Neal I appreciate it

@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 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 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.

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

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

@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.

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 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 Thanks for the detailed writeup, I'm sure this will help lots of people

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

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.

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

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

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?

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.

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

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

  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.

Hello @nba94. Did you mean something like this?

image

Or like this?

image

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

I was facing the same issue. I found in the examples folder an example for electron + eel that helped me to fix it, my problem was using the HTML file in the main.js file. but eel creates the eel.js in localhost:PORT/eel.js or something like that. but not in the www folder.
so you should change from mainWindow.loadfile('index.html'); to mainWindow.loadURL('http://localhost:8000/index.html); in the main.js file.

also, I found out that using URL ends the python script after closing the electron app. No idea why.

My way to use electron with eel.

  1. install Electron using npm in a folder. it will create _"node_modules/Electron/..."_ folder.

  2. eel searches for _"../node_modules/Electron/..."_ to execute the electron browser. so you should make a sub-folder beside the node_model folder, now your main folder should contain node_modules and app folders

  3. in eel.start function just add mode='electron'

for me I found an error in the electron.py file in the eel in line 18. bat_path was not working. I changed it in the join function to just empty "" and it worked. I'm not sure what it does so I didn't want to send any edit with it.

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

I was facing the same issue. I found in the examples folder an example for electron + eel that helped me to fix it, my problem was using the HTML file in the main.js file. but eel creates the eel.js in localhost:PORT/eel.js or something like that. but not in the www folder.
so you should change from mainWindow.loadfile('index.html'); to mainWindow.loadURL('http://localhost:8000/index.html); in the main.js file.

also, I found out that using URL ends the python script after closing the electron app. No idea why.

My way to use electron with eel.

  1. install Electron using npm in a folder. it will create _"node_modules/Electron/..."_ folder.
  2. eel searches for _"../node_modules/Electron/..."_ to execute the electron browser. so you should make a sub-folder beside the node_model folder, now your main folder should contain node_modules and app folders
  3. in eel.start function just add mode='electron'

for me I found an error in the electron.py file in the eel in line 18. bat_path was not working. I changed it in the join function to just empty "" and it worked. I'm not sure what it does so I didn't want to send any edit with it.

Were you able to package that electron app.
I can run electron app but i am not able to package it

actually no, I had tried to pack it but it didn't work, but also I didn't try again because I was going to use it in a developing env. so it was ok for me

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sameera-madushan picture sameera-madushan  路  3Comments

alifeinbinary picture alifeinbinary  路  3Comments

ClayMDMI picture ClayMDMI  路  4Comments

ghost picture ghost  路  6Comments

abdurahman-shiine picture abdurahman-shiine  路  3Comments