Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Vite React Example #671

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions examples/11 - ViteReact_ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
main.spec

node_modules
dist
dist_vite
build
dist-ssr
*.local
env

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Binary file added examples/11 - ViteReact_ts/Demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions examples/11 - ViteReact_ts/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
> "Eello World example": Vite, React, Typescript and Eel

**Table of Contents**

<!-- TOC -->

- [11 - CreateReactApp Documentation](#07---createreactapp-documentation)
- [Features](#features)
- [Quick Start](#quick-start)
- [About](#about)
- [Main Files](#main-files)

<!-- /TOC -->

# 11 - ViteReact-TS Documentation

Eello World example Vite React Typescript with Eel. This example is a newer version of example [07 - CreateReactApp](https://github.com/python-eel/Eel/tree/main/examples/07%20-%20CreateReactApp) by @KyleKing. This particular project was bootstrapped with `npm create vite@latest ViteReactTS --template react-ts` (Typescript enabled) and `Python 3.11`.

## Features

- **Multiple instances** To enable multiple instances of this application in production (i.e. the user opens the application multiple times), a new free socket port is searched for on app startup and injected in to `./dist_vite/index.html` and `./dist_vite/assets/index.*.js` before eel.init().
- **Splashscreen** A default splash-screen is provided so the user had direct feedback when the .exe file opened

## Quick Start

1. **Configure:** In the app's directory, run `npm install` and `pip install virtualenv`
2. **Virtual envirioment** Create a new virtual envirioment using `python -m venv env`. Open a new powershell window in the project directory and run `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser` to enable running venv activate script. Then activate the virtual envirioment with venv using `.\env\Scripts\activate.ps1`. Now using this virtual env run `pip install -r requirements.txt` See the footnote about Bottle.py!
3. **Demo:** Build static files with `npm run build` then run the application with `python main.py` from the venv powershell window. A Chrome-app window should open running the built code from `dist_vite/`
4. **Distribute:** (Run `npm run build` first) Build a binary distribution with PyInstaller using `python -m eel main.py dist_vite --onedir --splash splashfile.png --path env/lib/site-packages --noconsole` from the venv powershell window (See more detailed PyInstaller instructions at bottom of [the main README](https://github.com/ChrisKnott/Eel)). The .exe will be generated in `.\dist\main\main.exe`. Try to open two instances of the application, you will find that it just works :)
5. **Develop:** Open two prompts. In one, run `python main.py true` and the other, `npm run dev`. A browser window should open in your default web browser at: [http://localhost:5173/](http://localhost:5173/). As you make changes to the JavaScript in `src/` the browser will reload. Any changes to `main.py` will require a restart to take effect. You may need to refresh the browser window if it gets out of sync with eel.

**Note** # Bottle has a issue with stdout when using pyinstaller --noconsole. The latest developent version of bottle `0.13-dev` (not available on pypi) has a fix for this issue. In env/site-packages replace Bottle.py with bottle=`0.13-dev`: [https://github.com/bottlepy/bottle/blob/master/bottle.py](https://github.com/bottlepy/bottle/blob/master/bottle.py)

![Demo.png](Demo.png)

## About

> Use `window.eel.expose(func, 'func')` to circumvent `npm run build` code mangling

`npm run build` will rename variables and functions to minimize file size renaming `eel.expose(funcName)` to something like `D.expose(J)`. The renaming breaks Eel's static JS-code analyzer, which uses a regular expression to look for `eel.expose(*)`. To fix this issue, in your JS code, convert all `eel.expose(funcName)` to `window.eel(funcName, 'funcName')`. This workaround guarantees that 'funcName' will be available to call from Python.

## Main Files

Critical files for this demo

- `web_src/App.tsx`: Modified to demonstrate exposing a function from JavaScript and how to use callbacks from Python to update React GUI
- `main.py`: Basic `eel` file
- If run without arguments, the `eel` script will load `index.html` from the dist_vite/ directory (which is ideal for building with PyInstaller/distribution)
- If any 2nd argument (i.e. `true`) is provided, the app enables a "development" mode and attempts to connect to the React server on port 5173
- `web_src/index.html`: Added location of `eel.js` file based on options set in main.py

```html
<!-- Load eel.js from the port specified in the eel.start options -->
<script type="text/javascript" src="http://localhost:5169/eel.js"></script>
```

113 changes: 113 additions & 0 deletions examples/11 - ViteReact_ts/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""Main Python application file for the EEL-CRA demo.

To build for production:
npm run build
python -m eel main.py dist_vite --onefile --splash splashfile.png --path env/lib/site-packages --noconsole
"""

import os
import platform
import random
import sys
import importlib
import socket, errno
from py_src.contrib.replace_in_file import replaceInfile, findFileRe
from py_src.contrib.port_check import find_unused_port

import eel

if '_PYIBoot_SPLASH' in os.environ and importlib.util.find_spec("pyi_splash"):
import pyi_splash
pyi_splash.update_text('UI Loaded ...')
pyi_splash.close()


@eel.expose # Expose function to JavaScript
def say_hello_py(x):
"""Print message from JavaScript on app initialization, then call a JS function."""
print('Hello from %s' % x) # noqa T001
eel.say_hello_js('Python {from within say_hello_py()}!')


@eel.expose
def expand_user(folder):
"""Return the full path to display in the UI."""
return '{}/*'.format(os.path.expanduser(folder))


@eel.expose
def pick_file(folder):
"""Return a random file from the specified folder."""
folder = os.path.expanduser(folder)
if os.path.isdir(folder):
listFiles = [_f for _f in os.listdir(folder) if not os.path.isdir(os.path.join(folder, _f))]
if len(listFiles) == 0:
return 'No Files found in {}'.format(folder)
return random.choice(listFiles)
else:
return '{} is not a valid folder'.format(folder)


def start_eel(develop):
"""Start Eel with either production or development configuration."""

if develop:
directory = 'web_src'
app = None
page = {'port': 5173}
eel_port = 5169
else:
directory = 'dist_vite'
app = 'chrome'
page = 'index.html'

# find a unused port to host the eel server/websocket
eel_port = find_unused_port()

# replace the port in the web files
replace_file = findFileRe("./dist_vite/assets", "index.*.js")
replaceInfile(f"./dist_vite/assets/{replace_file}", 'ws://localhost:....', f"ws://localhost:{eel_port}")
replaceInfile("./dist_vite/index.html", 'http://localhost:.....eel.js', f"http://localhost:{eel_port}/eel.js")





eel.init(directory, ['.tsx', '.ts', '.jsx', '.js', '.html'])

# These will be queued until the first connection is made, but won't be repeated on a page reload
say_hello_py('Python World!')
eel.say_hello_js('Python World!') # Call a JavaScript function (must be after `eel.init()`)

eel.show_log('https://github.com/samuelhwilliams/Eel/issues/363 (show_log)')



eel_kwargs = dict(
host='localhost',
port=eel_port,
size=(1280, 800),
)
try:
eel.start(page, mode=app, **eel_kwargs)

except EnvironmentError:
# If Chrome isn't found, fallback to Microsoft Edge on Win10 or greater
if sys.platform in ['win32', 'win64'] and int(platform.release()) >= 10:
eel.start(page, mode='edge', **eel_kwargs)
else:
raise









if __name__ == '__main__':
import sys

# Pass any second argument to enable debugging
start_eel(develop=len(sys.argv) == 2)
Loading