🤖

Streamlit アプリを簡単にデスクトップアプリ化するコマンドを作った

2024/12/15に公開

Streamlit は簡単にデータサイエンス系のウェブアプリを構築できる Python ライブラリです。ウェブアプリなので、サーバーで起動してブラウザでアクセスすることになりますが、ローカルで動作するデスクトップアプリにして配布したい状況がありました。 Streamlitアプリをデスクトップアプリ化する方法はいくつかある[1][2]ようでしたが、制限があったり手間がかかったりするようでした。

「いや、お手軽にアプリにしたいんじゃっ」ということで、簡単にデスクトップアプリ化できる Streamlit Desktop App を作りました。このコマンドを使用すると、コマンド一発で Streamlitアプリをデスクトップアプリに変換できます[3]。ウィンドウを閉じたときに Stremlit のサーバープロセスが自動で Kill されるようにしたりと、細かい工夫も入っています。

なお、デスクトップアプリ化によるメリットには、以下のようなものがあります。

メリット 説明
配布が簡単 サーバー環境を用意することなく、ユーザーに使ってもらえます
ローカルのリソースを利用できる ローカルのファイルやデータを処理したいときに、アップロードする必要がありません
オフラインで利用可能 サーバーに接続する必要がないので、ネット環境が不安定でも利用できます

また、以下のような注意点も考えられるので、実際にデスクトップアプリを作る場合には意識しておくと良いでしょう。

注意点 説明
起動が遅い PyInstaller を使用しているため、起動に時間がかかる場合があります。--splash オプションを使って起動中であることがわかるようにするなどの工夫をするとよさそうです。
セキュリティ周りの注意 OAuth2 などを使用する場合、適切なフローで実装する必要があります。また、デスクトップアプリでは、ユーザーがアプリ内部に簡単にアクセスできます。アクセストークンの取り扱いなど、ウェブアプリとは異なる観点での注意が必要です。

インストール方法

まずは以下のコマンドで Streamlit Desktop App をインストールしておきます。

pip install streamlit-desktop-app

poetry を使っている場合は

poetry add --group dev streamlit-desktop-app

で開発用ライブラリとしてインストールできますが、依存ライブラリの関係で python = "^3.9,!=3.9.7,<3.13" となっているので注意してください[4]

使い方

以下のような Streamlit スクリプト app.py があるとします。

app.py
import streamlit as st

st.write("Hello!")

スクリプトのあるディレクトリで、以下のコマンドを打ちます。

streamlit-desktop-app build app.py --name "app"

すると、PyInstallerによって ./dist/app/ 以下に実行可能ファイルが生成されます。

./dist/app/app

simple

dist/app ディレクトリには、 実行可能ファイルの app 以外に _internal というディレクトリもできていて、依存ライブラリなどはそちらに収められています。配布したい場合は _internal を含めて zip などで固めて、ユーザーに展開してもらいます。

$ ls ./dist/app
_internal        app
$ cd ./dist
$ zip -r app.zip app
  adding: app/ (stored 0%)
  adding: app/_internal/ (stored 0%)
  ...

実行ファイル1つにまとめる

_internal を含むzipファイルを展開してもらうのが嫌な場合は、 PyInstaller の --onefile オプションを使います。

streamlit-desktop-app build app.py --name "app" --pyinstaller-options --onefile

すると、dist 配下に実行可能ファイルが1つできます。依存ファイルなども含められているので、このファイルを配布するだけで、ユーザーはアプリを利用できます。

$ cd dist
$ ls
app

--onefileをつけると、実行の度に一時ディレクトリを作成し、そこに全てのファイルを展開してから実行するようになります。そのため、起動時間が(PyInstallerで作られたアプリは一般に起動が遅いのですが)さらに遅くなります。また、一次ディレクトリは実行毎に異なるので、前回実行時にキャッシュとして作成したファイルは、次回以降の実行時に使えないことがあります[5]
ファイルを展開する手間が気にならないのであれば、 --onefile オプションは使わないことをお勧めします。

複数ファイル

Streamlit にはページ機能があり、pages ディレクトリ配下にスクリプトファイルを置いて複数ページのアプリを作ることができます。

$ tree -L 2
.
├── app.py
└── pages
    ├── page01.py
    └── page02.py

複数ページのアプリをデスクトップアプリ化するには、

  1. 各スクリプトファイルを含める
  2. 各スクリプトの依存ライブラリを含める

の2つが必要になります。これには、 PyInstaller の --add-data オプションと --hidden-import オプションが使えます。

streamlit-desktop-app build app.py --name "multipage" \
  --pyinstaller-options \
  --add-data pages:pages \
  --hidden-import pages.page01 \
  --hidden-import pages.page02

multipage

非 Pure Python なアプリ

使用しているライブラリが Pure Python ではない場合、 PyInstaller が必要なファイルを見落としてしまう場合があります。そういうケースでは、追加で必要なデータやバイナリを手動で指定する必要があります。

例えば、数理最適化アドベントカレンダーのネタとして作ったナンプレ(数独)の問題を生成するアプリ Number Place では、内部で PuLP (pulp)を利用しています。 PuLP は数理最適化のモデラーですが、利便性のためにデフォルトのソルバーも同梱した状態で配布しています。ソルバーは Python で書かれているわけではないので、 PyInstaller は依存関係の抽出に失敗してしまいます。

最も簡単な方法は、 pulp パッケージ配下のファイルを全て含めることです。これは PyInstaller の --collect-all オプションが使えます。

# poetry add --group dev streamlit-desktop-app
poetry run streamlit-desktop-app build app/main.py --name "Number Place" \
  --pyinstaller-options --collect-all pulp

Number Place

まとめ

簡単に Streamlit アプリをデスクトップアプリ化できるコマンド Streamlit Desktop App を作ったので使ってみてください!

脚注
  1. Streamlit の Wasmポートである stlite と Electron を組み合わせる方法があります。 ↩︎

  2. PyInstaller を使う方法もあります。今回はこちらの記事を参考にさせていただきました。 ↩︎

  3. 内部的に PyInstaller を使っているので、配布先の環境と同じOSで実行する必要があります。例えば、Windowsアプリを作りたい場合は、開発環境も Windows である必要があります。 ↩︎

  4. 例えば、pyproject.tomlpython = "^3.10" と指定されている場合は、 python = "^3.10,<3.13" とする必要があります。 ↩︎

  5. 保存先のパスを明示的に指定するなどの工夫が必要です ↩︎

Discussion