For little HTML GUI applications, with easy Python/JS interop
npx @tessl/cli install tessl/pypi-eel@0.18.0A little Python library for making simple Electron-like offline HTML/JS GUI apps, with full access to Python capabilities and libraries. Eel hosts a local webserver, then lets you annotate functions in Python so that they can be called from JavaScript, and vice versa.
pip install eelpip install eel[jinja2] (for HTML templating)import eelFunction-specific imports:
from eel import expose, init, start, show, sleep, spawnimport eel
# Set web files folder
eel.init('web')
# Expose Python function to JavaScript
@eel.expose
def say_hello_py(name):
print(f'Hello from {name}')
# Start the app with a browser window
eel.start('index.html', size=(800, 600))Eel bridges Python and JavaScript through a WebSocket connection:
@eel.expose become callable from JavaScripteel.expose() become callable from PythonThis design enables seamless integration between Python's scientific computing capabilities and JavaScript's visualization libraries while maintaining the simplicity needed for desktop GUI applications.
Essential functions for initializing and running Eel applications, including setup, function exposure, and application lifecycle management.
def init(path: str, allowed_extensions: List[str] = ['.js', '.html', '.txt', '.htm', '.xhtml', '.vue'], js_result_timeout: int = 10000) -> None: ...
def expose(name_or_function: Optional[Callable[..., Any]] = None) -> Callable[..., Any]: ...
def start(*start_urls: str, mode: Optional[Union[str, Literal[False]]] = 'chrome', host: str = 'localhost', port: int = 8000, block: bool = True, jinja_templates: Optional[str] = None, cmdline_args: List[str] = ['--disable-http-cache'], size: Optional[Tuple[int, int]] = None, position: Optional[Tuple[int, int]] = None, geometry: Dict[str, Tuple[int, int]] = {}, close_callback: Optional[Callable[..., Any]] = None, app_mode: bool = True, all_interfaces: bool = False, disable_cache: bool = True, default_path: str = 'index.html', app: btl.Bottle = btl.default_app(), shutdown_delay: float = 1.0, suppress_error: bool = False) -> None: ...
def show(*start_urls: str) -> None: ...Functions for managing asynchronous execution and non-blocking operations within the Gevent event loop.
def sleep(seconds: Union[int, float]) -> None: ...
def spawn(function: Callable[..., Any], *args: Any, **kwargs: Any) -> Greenlet: ...Browser-specific modules and functions for launching applications in different browsers with custom configurations.
def register_eel_routes(app: btl.Bottle) -> None: ...
# From eel.browsers module
def set_path(browser_name: str, path: str) -> None: ...
def get_path(browser_name: str) -> Optional[str]: ...
def open(start_pages: Iterable[Union[str, Dict[str, str]]], options: OptionsDictT) -> None: ...Browser modules available:
eel.chrome - Google Chrome/Chromium supporteel.electron - Electron supporteel.edge - Microsoft Edge supporteel.msIE - Internet Explorer supportCommand-line interface for building distributable executables using PyInstaller integration.
# Command-line interface
python -m eel main_script web_folder [PyInstaller arguments...]from typing import Union, Dict, List, Tuple, Optional, Callable, Any
from typing_extensions import Literal, TypedDict
from bottle import Bottle
OptionsDictT = TypedDict('OptionsDictT', {
'mode': Optional[Union[str, Literal[False]]],
'host': str,
'port': int,
'block': bool,
'jinja_templates': Optional[str],
'cmdline_args': List[str],
'size': Optional[Tuple[int, int]],
'position': Optional[Tuple[int, int]],
'geometry': Dict[str, Tuple[int, int]],
'close_callback': Optional[Callable[..., Any]],
'app_mode': bool,
'all_interfaces': bool,
'disable_cache': bool,
'default_path': str,
'app': Bottle,
'shutdown_delay': float,
'suppress_error': bool,
'jinja_env': JinjaEnvironmentT
}, total=False)
# Type aliases
JinjaEnvironmentT = Any # jinja2.Environment when available
WebSocketT = Any # geventwebsocket.websocket.WebSocket
Greenlet = Any # gevent.Greenlet