This readme outlines the steps to create executables for macOS, Linux, and Windows using Python, Poetry, and PyInstaller. The process is similar across platforms with some platform-specific adjustments.
Before starting, it's good to clean up any previous builds:
rm -rf dist build *.spec
sudo rm -rf /mnt/c/MyFiles/050-FDSN/seed-vault/.venv
For each platform (Linux, macOS, Windows):
- Install Python from python.org.
- Install Poetry by running the following command:
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
- Install PyInstaller using Poetry or pip:
poetry add --dev pyinstaller
Once Poetry is installed, install all dependencies for your project:
poetry install
poetry shell
poetry show --with dev
[https://github.com/jvcss/PyInstallerStreamlit]
Create a file called run_app.py
with the following content:
from streamlit.web import cli
if __name__ == '__main__':
cli._main_run_clExplicit('seed_vault/ui/main.py', is_hello=False)
Navigate to the Streamlit path in your virtual environment:
.env\Lib\site-packages\streamlit\web\cli.py
Add the following function to cli.py
:
def _main_run_clExplicit(file, is_hello, args=[], flag_options={}):
bootstrap.run(file, is_hello, args, flag_options)
Create a new hook file hook-streamlit.py
under the ./hooks
directory:
from PyInstaller.utils.hooks import copy_metadata
datas = copy_metadata('streamlit')
Use the following command to compile the app on Linux:
pyinstaller --name seed-vault \
--onefile \
--additional-hooks-dir=./hooks \
--collect-all streamlit \
--collect-all folium \
--collect-all obspy \
--collect-all streamlit-folium \
--specpath . \
--clean \
run_app.py
For Windows, use PowerShell and the following command:
pyinstaller --name seed-vault `
--onefile `
--additional-hooks-dir=.\hooks `
--collect-all streamlit `
--collect-all folium `
--collect-all obspy `
--collect-all streamlit-folium `
--specpath . `
--clean `
run_app.py
For macOS, the command is similar to Linux:
pyinstaller --name seed-vault \
--onefile \
--additional-hooks-dir=./hooks \
--collect-all streamlit \
--collect-all folium \
--collect-all obspy \
--collect-all streamlit-folium \
--specpath . \
--clean \
run_app.py
Create the following configuration file for Streamlit to specify server options.
[global]
developmentMode = false
[server]
port = 8502
Add this file either to the project root or the dist
output folder after building.
After building, copy configuration and source files to the dist
folder:
xcopy /s /e ".\.streamlit" "dist\.streamlit"
Copy-Item -Path "seed_vault" -Destination "dist\seed_vault" -Recurse
For Linux, the datas
and hiddenimports
sections in the .spec
file should look like this:
datas = datas + [
(".venv/lib/python3.12/site-packages/altair/vegalite/v5/schema/vega-lite-schema.json", "./altair/vegalite/v5/schema/"),
(".venv/lib/python3.12/site-packages/streamlit/static", "./streamlit/static"),
(".venv/lib/python3.12/site-packages/streamlit/runtime", "./streamlit/runtime"),
(".venv/lib/python3.12/site-packages/streamlit_folium", "streamlit_folium"),
('.venv/lib/python3.12/site-packages/obspy/RELEASE-VERSION', 'obspy/'),
("seed_vault", "seed_vault"),
("data", "data"),
(".streamlit/config.toml", ".streamlit/config.toml"),
]
hiddenimports = hiddenimports + ['tqdm', 'streamlit-folium', 'pandas', 'requests', 'click', 'tabulate', 'numpy', 'matplotlib', 'pydantic']
For Windows, adjust the paths to Windows-style:
datas = datas + [
(".venv\Lib\site-packages\altair\vegalite\v5\schema\vega-lite-schema.json", "./altair/vegalite/v5/schema/"),
(".venv\Lib\site-packages\streamlit\static", "./streamlit/static"),
(".venv\Lib\site-packages\streamlit\runtime", "./streamlit/runtime"),
(".venv\Lib\site-packages\streamlit_folium", "streamlit_folium"),
('.venv\Lib\site-packages\obspy\RELEASE-VERSION', 'obspy/'),
("seed_vault", "seed_vault"),
("data", "data"),
(".streamlit\config.toml", ".streamlit/config.toml"),
]
hiddenimports = hiddenimports + ['tqdm', 'streamlit-folium', 'pandas', 'requests', 'click', 'tabulate', 'numpy', 'matplotlib', 'pydantic']
For macOS, use the same style as Linux, with paths similar to Linux paths:
datas = datas + [
(".venv/lib/python3.12/site-packages/altair/vegalite/v5/schema/vega-lite-schema.json", "./altair/vegalite/v5/schema/"),
(".venv/lib/python3.12/site-packages/streamlit/static", "./streamlit/static"),
(".venv/lib/python3.12/site-packages/streamlit/runtime", "./streamlit/runtime"),
(".venv/lib/python3.12/site-packages/streamlit_folium", "streamlit_folium"),
('.venv/lib/python3.12/site-packages/obspy/RELEASE-VERSION', 'obspy/'),
("seed_vault", "seed_vault"),
("data", "data"),
(".streamlit/config.toml", ".streamlit/config.toml"),
]
hiddenimports = hiddenimports + ['tqdm', 'streamlit-folium', 'pandas', 'requests', 'click', 'tabulate', 'numpy', 'matplotlib', 'pydantic']
Finally, to build the executable on any platform, run:
pyinstaller run_app.spec --clean
By following these steps, you can build standalone executables for macOS, Linux, and Windows using Poetry and PyInstaller. Each platform requires slight adjustments, such as path formatting, but the overall process remains consistent across environments.
@stlite/desktop can be used to build a desktop app with Steamlit. Follow the below step:
- Create a
package.json
. There is already one created in the project (see https://github.com/whitphx/stlite/blob/main/packages/desktop/README.md) - Make sure all required project files are available in
package.json
- Make sure
requirements.txt
is available for lib dependencies npm install
-> this will install required node_modulesnpm run dump
-> builds the appnpm run serve
-> serves the appnpm run dist
-> creates executable
stlite only accept libraries that have pure wheels, i.e., they are built for webassembly. pyodide
(https://pyodide.org/en/stable/usage/faq.html#why-can-t-micropip-find-a-pure-python-wheel-for-a-package) seems to be reponsible to bundle the app. This library already comes with a list of famous libs such as pandas
. But it does not support less famous/widely used libs such as obspy
.
Packagin a lib in to a webassembly wheel seems to be quite complicated and not worthy of much try. And this is the main blocker of this approach.
NOTE: packages with pure wheel will have *py3-none-any.whl
in their naming.