LoginSignup
4
10

StreamlitのEXE化

Last updated at Posted at 2024-02-21

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

また、例えば以下のようなモジュールをインポートする場合、
下のモジュール名まで書かないとプログラムが実行されない場合がある。

参考:https://stackoverflow.com/questions/35060523/how-to-use-pyinstaller-with-hidden-imports-for-scipy-optimize-leastsq

"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の結果の値を表示

sfigs01.png

追記

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"),
4
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
10