Framework
Streamlit is a Python framework for machine learning and data visualization. With just a few lines of code, you can build a polished online app. For front-end newbies, it completely removes the need to learn any of those difficult front-end frameworks. Building APIs with Flask on the backend is easy enough, but making a front-end look good means learning a bunch of frameworks and knowing how to polish them — Streamlit solves that pain point.
However, Streamlit natively can only be deployed on a server and viewed through a browser, which won't work for industrial software. A Japanese dev built a wrapper framework so Streamlit projects can be packaged as desktop applications.
Environment Setup
It's assumed you already know about and have installed nvm plus a basic Node.js environment. If not, see another blog post of mine: Blog Frontend Redevelopment - Saturn Ring Base.
It's also assumed you know how to create Python virtual environments and the basic usage of pip. These are Python fundamentals.
- Following the tutorial in the Japanese dev's repo, create a new folder as your project folder. Create the following
package.jsonto start a new NPM project, and edit thenamefield.
{
"name": "xxx",
"version": "0.1.0",
"main": "./build/electron/main.js",
"scripts": {
"dump": "dump-stlite-desktop-artifacts",
"serve": "cross-env NODE_ENV=production electron .",
"app:dir": "electron-builder --dir",
"app:dist": "electron-builder",
"postinstall": "electron-builder install-app-deps"
},
"build": {
"files": ["build/**/*"],
"directories": {
"buildResources": "assets"
}
},
"devDependencies": {
"@stlite/desktop": "^0.69.2",
"cross-env": "^7.0.3",
"electron": "33.3.1",
"electron-builder": "^25.1.7"
},
"stlite": {
"desktop": {
"files": ["app.py"],
"entrypoint": "app.py"
}
}
}
- Run
npm install
At this point you'll run into errors like npm error Cannot read properties of null (reading 'matches') or npm error RequestError: unable to verify the first certificate. You need to delete the existing node_modules folder in the project folder, set the npm registry mirror and the Electron mirror, and clear the npm cache.
Since there's no decent command-line way to change the Electron mirror (the command-line advice online is all nonsense), on Windows you'll need to edit the .npmrc file in your C:\Users\your username folder to the following:
registry=https://registry.npmmirror.com
strict-ssl=false
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
Then run the following command to clear the cache, and npm install should work fine.
npm cache clean --force
- Create
app.pyand write the Streamlit application code.
Since the stlite.desktop.files and stlite.desktop.entrypoint configs in package.json specify app.py, the file must be named app.py.
stlite.desktop.files specifies which files and folders get copied and bundled into the desktop app, and stlite.desktop.entrypoint specifies the entry-point Streamlit application.
Here's an example:
import altair as alt
import numpy as np
import pandas as pd
import streamlit as st
"""
# Welcome to Streamlit!
Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
forums](https://discuss.streamlit.io).
In the meantime, below is an example of what you can do with just a few lines of code:
"""
num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
indices = np.linspace(0, 1, num_points)
theta = 2 * np.pi * num_turns * indices
radius = indices
x = radius * np.cos(theta)
y = radius * np.sin(theta)
df = pd.DataFrame({
"x": x,
"y": y,
"idx": indices,
"rand": np.random.randn(num_points),
})
st.altair_chart(alt.Chart(df, height=700, width=700)
.mark_point(filled=True)
.encode(
x=alt.X("x", axis=None),
y=alt.Y("y", axis=None),
color=alt.Color("idx", legend=None, scale=alt.Scale()),
size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
))
The 4 packages it requires — I'd recommend creating a fresh virtual environment and installing them in it with commands like python -m pip install altair.
- Add more files or directories
You can edit the stlite.desktop.files config in package.json as follows. These directories and .py files should all follow Streamlit's application conventions — in short, whatever files your Python project uses, just add them all:
{
// ...other fields...
"stlite": {
"desktop": {
// ...other fields...
"files": ["app.py", "pages/*.py", "assets"]
}
}
}
- Specify which packages are installed in the desktop app
You can edit the stlite.desktop.dependencies config in package.json as follows — in short, whatever packages your Python project uses, just add them all:
{
// ...other fields...
"stlite": {
"desktop": {
// ...other fields...
"dependencies": ["altair", "numpy", "pandas", "streamlit"]
}
}
}
You can also edit stlite.desktop.requirementsTxtFiles as follows. This is Python's standard dependency file, and you can specify a list of dependencies with it:
{
// ...other fields...
"stlite": {
"desktop": {
// ...other fields...
"requirementsTxtFiles": ["requirements.txt"]
}
}
}
- Enable the Node worker thread
I still haven't figured out what the so-called worker thread is. From the behavior I've seen, without the worker thread there's no ability to run Python code — Pyodide doesn't start up properly, and the Streamlit app won't launch.
Edit stlite.desktop.nodeJsWorker as follows:
{
// ...other fields...
"stlite": {
"desktop": {
"nodeJsWorker": true
}
}
}
- Use the
npm run dumpcommand
This creates the ./build directory with a pile of miscellaneous stuff, all of it needed by the app framework. But it all serves the development server that comes later — none of it can be published as an executable.
- Use the
npm run servecommand
This command is just a wrapper around the electron command — you can see the actual command in package.json. It launches Electron and runs the app at ./build/electron/main.js, which is what the "main" field of package.json points to. In effect, it starts a development server and opens a desktop window for preview.
- Use the
npm run app:distcommand
This is likewise a wrapper around an electron command. It takes all the miscellaneous stuff in the ./build directory and assembles it into an installer, placed in the ./dist folder. electron-builder has a much more detailed explanation.
- The more suitable option is
npm run app:dir, which generates a portable (no-install-needed) application in the./builddirectory.
Afterword
This framework can't serve as a simple local application — it requires a full server-side backend. In my case, I abandoned it because I couldn't use the loopback address for socket.io communication.
According to the same Japanese dev — Electron security best practices by whitphx · #445 · whitphx/stlite — this doesn't follow Electron security best practices.
On top of that, his wrapper actually downloads the packages specified in our package.json and runs them in a sandbox using the Python version he's pinned. But industrial desktop apps need to support 32-bit systems, and Streamlit's roadmap doesn't support 32-bit systems either — you'd be limited to the old 0.62.0 version. So I gave up on the whole framework.
