Pythonで作成したツールの公開・配布を考えた場合にベストな開発構成は何かを考えた次第です。
言語はやはりライブラリが豊富なPythonがよい。GUIはやはり必要なのでGUIフレームワークを利用します。
Streamlit
Flet
NiceGUI
などを検討しましたが、ネイティブアプリ化がしやすく、UIのカスタマイズも容易な NiceGUI
がベストでした。
準備
ryeを導入しておきます。
以降のステップは Windows環境 を想定しています。最終的にアプリのexeファイルを作るからですが、どのような環境でもほぼ同じ手順だと思います。
1.プロジェクトを作る
rye で project を作ります。名前は適当。
❯ mkdir niceapp-hoge
❯ cd .\niceapp-hoge\
❯ rye init --script
必要な依存関係を追加してsyncする。
❯ rye add nicegui pywebview
❯ rye sync
ツリーはこんな感じ
.
│ .gitignore
│ .python-version
│ pyproject.toml
│ README.md
│ requirements-dev.lock
│ requirements.lock
├─.venv
└─src
└─niceapp_hoge
__init__.py
__main__.py
Python仮想環境にはプロジェクト名と同じ実行可能スクリプトが定義されていて、実行してみると
❯ rye run niceapp-hoge
Hello from niceapp-hoge!
となり、これは src/niceapp_hoge/__init__.py
の main
を実行している。
def main() -> int:
print("Hello from niceapp-hoge!")
return 0
2.NiceGUIアプリを作る
__init__.py
の main
から起動できる NiceGUIアプリを作る。以下は例。
from nicegui import ui, app
def exitApp():
app.shutdown()
class ViewMain:
def __init__(self):
pass
def view(self):
with ui.header().classes("p-2"):
with ui.row().classes("items-center fit"):
ui.label("Hello from niceapp-hoge!")
ui.space()
ui.icon("cancel", size="sm").on("click", handler=lambda: exitApp())
ui.label("This is a niceapp-hoge example.")
with ui.footer().classes("p-2"):
ui.space()
ui.label("Footer")
def main() -> int:
viewMain = ViewMain()
viewMain.view()
ui.run(
reload=False,
native=True,
frameless=True,
show_welcome_message=False,
)
return 0
native=True
と frameless=True
を指定しておくと、ブラウザ上ではなくネイティブアプリのようにアプリを起動することができる。
モジュール実行(ryeで定義されたスクリプトから実行すると)では ui.run
で reload=False
にしておかないとではうまく動作しない。これはホットリロードを無効にする指定なので、開発時は別の起動用スクリプトを用意しておいて、直接実行するのがよい。例えば、以下のようなコードを用意しておいて、開発時は python app.py
として実行する。
from niceapp_hoge import ViewMain
from nicegui import ui
viewMain = ViewMain()
viewMain.view()
ui.run(
reload=True,
native=True
frameless=True,
show_welcome_message=False
)
スクリプトを実行してみると
❯ rye run niceapp-hoge
いい感じ。以降はNiceGUIアプリを作りこんでいく。
3.pypiへ公開
NiceGUIアプリが完成したらpypiで公開する。pypiのAPIトークンを用意しておく。以下は参考。
必要に応じて、pyproject.toml
を修正(versionとかDescriptionとか)して、build
する。 -o
で出力先ディレクトリをdist
から変更しておく。あとで使う実行可能パッケージの作成で名前が重複してしまうため。
❯ rye build -o ./dist_build
building niceapp-hoge
* Creating isolated environment: venv+uv...
* Using external uv from C:\Users\xxxxx\.rye\uv\0.2.22\uv.EXE
* Installing packages in isolated environment:
- hatchling
* Getting build dependencies for sdist...
* Building sdist...
* Building wheel from sdist
* Creating isolated environment: venv+uv...
* Using external uv from C:\Users\xxxxxx\.rye\uv\0.2.22\uv.EXE
* Installing packages in isolated environment:
- hatchling
* Getting build dependencies for wheel...
* Building wheel...
Successfully built niceapp_hoge-0.1.0.tar.gz and niceapp_hoge-0.1.0-py3-none-any.whl
次にpublish
❯ rye publish
No access token found, generate one at: https://pypi.org/manage/account/token/
Access token: [APIトークンを貼り付けする]
Uploading distributions to https://upload.pypi.org/legacy/
Uploading niceapp_hoge-0.1.0-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8/4.8 kB • 00:00 • ?
Uploading niceapp_hoge-0.1.0.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.0/5.0 kB • 00:00 • ?
View at:
https://pypi.org/project/niceapp-hoge/0.1.0/
これだけでOK
試しに、別の仮想環境を作ってインストールしてみる。
❯ mkdir testenv
❯ cd .\testenv\
❯ python -m venv .venv
❯ .\.venv\Scripts\Activate.ps1
❯ pip install niceapp-hoge
アプリの起動はスクリプトを実行するだけ
❯ niceapp-hoge
これでNiceGUIアプリが起動する。ユーザー側でPython環境が整っている状況が想定できるのであれば、アプリの配布はこれで十分かもしれない。
4. Exeファイルの作成
ユーザ側にPython環境があることを想定できるのであればpypiで公開するのでもよいが、そうでないのであれば単独で実行可能なファイル(ここでは exeファイル)を配布したいということになる。NiceGUIは実行可能ファイルへのパッケージ用コマンドが用意されているのでそれを利用する。
PyInstallerを利用するので、依存関係を追加しておく。
❯ rye add pyinstaller
パッケージ用の起動スクリプトを書いておく。ui.run
の引数が異なるだけなので、先ほどの app.py
を書き換えるのでもよい。
from niceapp_hoge import ViewMain
from nicegui import ui, native
viewMain = ViewMain()
viewMain.view()
ui.run(
reload=False,
native=True,
frameless=True,
port=native.find_open_port(),
show_welcome_message=False
)
パッケージ用のコマンドを実行する
❯ rye run nicegui-pack --onefile --windowed --name "NiceAPP" .\pack.py
dist
ディレクトリに NiceAPP.exe
ができている。起動時間が多少かかるが単独で実行可能なアプリになる。
アイコンとかがダサいので、アイコンなどを変更したりするのであれば pyinstaller を自分で叩く必要があるかも。こだわらなければこれで十分でしょうか。