Stremlitで作成したプログラムをPyinstallerでExe化を行った。
基本的には、以下のサイトを参考にExe化したが、いくつか変更した点があるので覚えとして記載する。
PyInstallerでStreamlitアプリをexe/app化する
試した環境
・Windows 11 Pro
・miniconda
・Python 3.9
・Streamlit 1.20
・pyinstaller 5.0.1
(仮想環境はcondaで作成。最低限はCondaで入れて、それ以外はpipでモジュールを導入している)
# 例えば、仮想環境の構築
conda create -n st_app python=3.9
# 仮想環境ができたら、仮想環境に移動
conda activate st_app
# 仮想環境に入り
pip install streamlit numpy pyinstaller scipy seaborn
# などpipでインストール
作成方法と変更箇所
基本的には、以下のサイトを参考に作成。
PyInstallerでStreamlitアプリをexe/app化する
Specファイルの変更箇所を記載。
datasにある、
("altair/vegalite/v4/schema/vega-lite-schema.json"), "./altair/vegalite/v4/schema/",
Streamlitのversionによっては、v4ではなくv5の可能性があるので、その際にはv5に変更する必要がある。
C:\Users\Username...\envs\仮想環境名\Lib\site-packages\altair\vegalite以下のホルダーを確認。
hiddenimportにモジュールを記載。
モジュール名とsit-packageにあるホルダ名が異なるとエラーがでる。
例えば
scikit-learn ---> sklearn
Stremlit-aggrid ---> st_aggrid
また、例えば以下のようなモジュールをインポートする場合、
下のモジュール名まで書かないとプログラムが実行されない場合がある。
"sklearn.metrics",
"sklearn.metrics.r2_score",
"sklearn.metrics.mean_absolute_error",
"scipy.optimize",
"scipy.optimize.curve_fit",
自作モジュールをプログラムに含める場合
Mainのプログラムの他に自作のモジュールをmainプログラムでインポートする場合について
ホルダー構成
streamlit_app # フォルダ名は何でもよい
├── .streamlit/
│ └── config.toml
├── hooks/
│ └── hook-streamlit.py
├── main.py
├── my_module/ # 自作のモジュール
│ ├── __init__.py
│ └── my_module1.py
└── run_main.py
なお、自作モジュールで使うライブラリーもhiddenimportに含める必要がある。
config.toml、run_main.py、hook-streamlit.pyの記載は、参考サイトと同じ。
# main.py
import os
import sys
import pandas as pd
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from my_module import my_module1 # 自作モジュールのimportの記載方法
def main():
st.header('スタンドアロンアプリのデモ',)
val = my_module1.calc(5,10)
val2 = my_module1.fit_xy()
st.subheader('my_module calcの計算結果',str(val))
st.subheader('my_module Fittingの結果',str(val2[0]))
# グラフを作成する
fig = plt.figure(figsize=(6,3))
sns.set_theme(style="darkgrid")
fmri = sns.load_dataset("fmri")
sns.lineplot(x="timepoint", y="signal",
hue="region", style="event",
data=fmri)
# グラフを表示する
st.pyplot(fig)
if __name__ == '__main__':
main()
# my_module1.py
# 自作モジュール Mainからの関数呼び出しやScipyを使った関数のの呼び出しの例として
import numpy as np
from scipy.optimize import curve_fit
def base_func(x,a,b,c):
y = c + a*(x - b)**2
return y
def fit_xy():
x = np.arange(-30, 30, 1)
para = [2.0,5.0,10.0]
np.random.seed(seed=10)
y = base_func(x,para[0],para[1],para[2])+np.random.normal(0, 60, len(x))
popt, pcov = curve_fit(f=base_func, xdata=x, ydata=y, p0=para)
return popt, pcov
def calc(x,y):
return x+y
なお、Exe化する前にプログラムが動作するか確認した方がよい。
streamlit run main.py
Ctrl + C を押すと、Local hostが停止する。
# run_main.spec
# -*- mode: python ; coding: utf-8 -*-
import site
import os
block_cipher = None
assert len(site.getsitepackages()) > 0
package_path = site.getsitepackages()[0]
for p in site.getsitepackages():
if "site-package" in p:
package_path = p
break
a = Analysis(
['run_main.py'],
pathex=[],
binaries=[],
datas=[(os.path.join(package_path, "altair/vegalite/v4/schema/vega-lite-schema.json"), "./altair/vegalite/v4/schema/"),
(os.path.join(package_path, "streamlit/static"), "./streamlit/static"),
(os.path.join(package_path, "streamlit/runtime"), "./streamlit/runtime")],
hiddenimports=['seaborn','scipy.optimize.curve_fit'],
hookspath=['./hooks'],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='run_main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
Specファイルを修正したら
pyinstaller run_main.spec --clean
distホルダにできたrun_main.exeをmain.pyと同じ階層に移動してから実行する。
実行結果
タイトルの下に、
my_module1にある関数を用いて、
5と10の計算結果を表示
Fittingの結果の値を表示
追記
st_aggridを使ったプログラムをExe化する際に修正したSpecファイルの内容。
理由はよくわからないがとりあえず動作。
Specファイル
追加(1)
import sys
sys.setrecursionlimit(5000)
追加(2)
st_aggrid initファイルで読み込んでいるモジュールを追加
hiddenimportsに以下のモジュールを追加
"scipy",
"statsmodels",
"numpy",
"streamlit.components.v1",
datasに以下を追加
(os.path.join(package_path, "st_aggrid/frontend/build"), "./st_aggrid/frontend/build"),
(os.path.join(package_path, "st_aggrid/frontend/build/static"), "./st_aggrid/frontend/build/static"),