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

cx_freeze support in addition to PyInstaller #465

Open
wants to merge 2 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
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Eel is designed to take the hassle out of writing short and simple GUI applicati
- [Callbacks](#callbacks)
- [Synchronous returns](#synchronous-returns)
- [Asynchronous Python](#asynchronous-python)
- [Building distributable binary with PyInstaller](#building-distributable-binary-with-pyinstaller)
- [Building distributable binary with PyInstaller or cx_freeze](#building-distributable-binary-with-pyinstaller-or-cx_freeze)
- [Microsoft Edge](#microsoft-edge)

<!-- /TOC -->
Expand Down Expand Up @@ -335,19 +335,26 @@ while True:
2. The `my_other_thread` method, repeatedly printing **"I'm a thread"**
3. The main Python thread, which would be stuck in the final `while` loop, repeatedly printing **"I'm a main loop"**

## Building distributable binary with PyInstaller
## Building distributable binary with PyInstaller or cx_freeze

If you want to package your app into a program that can be run on a computer without a Python interpreter installed, you should use **PyInstaller**.
If you want to package your app into a program that can be run on a computer without a Python interpreter installed, you should use **PyInstaller**, or **cx_freeze**.

###PyInstaller
1. Configure a virtualenv with desired Python version and minimum necessary Python packages
2. Install PyInstaller `pip install PyInstaller`
3. In your app's folder, run `python -m eel [your_main_script] [your_web_folder]` (for example, you might run `python -m eel hello.py web`)
4. This will create a new folder `dist/`
5. Valid PyInstaller flags can be passed through, such as excluding modules with the flag: `--exclude module_name`. For example, you might run `python -m eel file_access.py web --exclude win32com --exclude numpy --exclude cryptography`
6. When happy that your app is working correctly, add `--onefile --noconsole` flags to build a single executable file
7. At your `eel.init()`, add `frozen_with='PyInstaller`

Consult the [documentation for PyInstaller](http://PyInstaller.readthedocs.io/en/stable/) for more options.

###cx_freeze
You can build your executable normally, into an EXE or an MSI. At your `eel.init()`, add `frozen_with=cx_freeze`

Consult the [documentation for cx_freeze](https://cx-freeze.readthedocs.io/en/latest/) for help on freezing your application.

## Microsoft Edge

For Windows 10 users, Microsoft Edge (`eel.start(.., mode='edge')`) is installed by default and a useful fallback if a preferred browser is not installed. See the examples:
Expand Down
11 changes: 7 additions & 4 deletions eel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ def decorator(function):


def init(path, allowed_extensions=['.js', '.html', '.txt', '.htm',
'.xhtml', '.vue'], js_result_timeout=10000):
'.xhtml', '.vue'], js_result_timeout=10000, frozen_with=None):
global root_path, _js_functions, _js_result_timeout
root_path = _get_real_path(path)
root_path = _get_real_path(path, frozen_with)

js_functions = set()
for root, _, files in os.walk(root_path):
Expand Down Expand Up @@ -306,9 +306,12 @@ def _process_message(message, ws):
print('Invalid message received: ', message)


def _get_real_path(path):
def _get_real_path(path, frozen_with):
if getattr(sys, 'frozen', False):
return os.path.join(sys._MEIPASS, path)
if frozen_with == 'cx_freeze':
return os.path.join(sys.executable, path)
elif frozen_with == 'PyInstaller':
return os.path.join(sys._MEIPASS, path)
Comment on lines +311 to +314

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be easier to just do

Suggested change
if frozen_with == 'cx_freeze':
return os.path.join(sys.executable, path)
elif frozen_with == 'PyInstaller':
return os.path.join(sys._MEIPASS, path)
return os.path.join(getattr(sys, '_MEIPASS', sys.executable), path)

That way, user can seemlessly switch between the two

else:
return os.path.abspath(path)

Expand Down