2023年末にリリースされた Flet のバージョン 0.18.0 では、以前からあった pack
より良いビルド方法 build
が導入されました。自分も macOS 用にビルドしてみたのですが、なぜか一つの環境では Hello, Flet とだけウィンドウに表示されるアプリがビルドされ、いろいろ試すも解決できず。そしてつい数日前の 2024/1/16 に、主に flet build
関連の bug fix がなされたバージョン 0.19.0 がリリースされたので、macOS 用デスクトップアプリをビルドしてみました (flet 0.21.2 で Audio コントロールを使用する場合のビルド方法は別記事にまとめました)。
リリースのあった公式 discord: https://discord.com/channels/981374556059086931/981375816489402408
公式 GitHub の change log: https://github.com/flet-dev/flet/blob/main/CHANGELOG.md
公式 flet build の解説ページ (英語): https://flet.dev/docs/guides/python/packaging-app-for-distribution
まずはビルド前の Python スクリプトを簡単にご紹介
パスワード生成アプリを作りました。今回は Flet のビルドの紹介をメインとするので、アプリ自体の細かい説明は省きます。が、簡単に何ができるかというと、好きな文字数で、好きな特殊文字を含んだ、ある程度強度のあるパスワードを生成することができます。生成されたパスワードはコピーボタンでクリップボードにコピーできます。Safari が作ってくれる強力なパスワードが使えないサイトや、もっと強度を強くしたい、等の用途で活躍します。パスワード強度をチェックしてくれるサイト (bitwarden 等) で強度を調べると解析されるまで数世紀かかる等と結果が出て面白いです。
コードは GitHub に置いてあります: https://github.com/tokyohandsome/passgen.py/blob/main/fletpassgen.py
環境
- macOS: 14.2.1
- 仮想環境 (pipenv): 2023.10.24
- Python: 3.11.6
- 追加モジュール: flet (バージョン: 0.19.0) 自動的に flet-core と flet-runtime も入っている
- Rosetta
- Xcode: 15.2
- Git: 2.29.2
- cocoapods: 1.14.3_1
- Flutter: 3.16.7
手順
- 仮想環境を作る
- flet をインストール (バージョン 0.18.0 以上、今回は最新の 0.19.0)
- コードを書く、もしくは持ってくる
- 動作確認
- Flutter とその他必要なものを一式インストール
- アプリ名のフォルダを作り、以降はその中で作業
- assets フォルダを作り、アイコン (icon.png) を格納
- 手順 3. のコードを main.py とリネームしてコピー
- main.py の最後、メイン関数を呼び出す行を
ft.app(main)
だけにする - requirements.txt に追加モジュールを書く
- ビルドテンプレートを GitHub からクローン
- ビルドテンプレートのコピーライト表記などを編集
- ビルド実行
部分的に細かく (手順 1~4)
仮想環境はお好きなのを使って良いと思います。Python は 3.8 以降、flet は特にバージョン指定せず pip install flet
で良いでしょう。自分で書いた Flet アプリのコードが無ければ、上記のボクの GitHub からコピーしたものを適当な名前 (例: fletpassgen.py) で保存してください。python3 fletpassgen.py
で動作することを確認したら、デスクトップアプリビルドに必要な諸々をインストールします。
Flutter とその他必要なものを一式インストール (手順 5)
ビルドには元祖である Flutter や開発言語の Dart とその他必要なもの一式をインストールする必要があります。基本的には一度済ませれば何度も行う必要はありません。以下は Apple Silicon (M1、M2、M3 シリーズ) の場合です。Flutter のサイトを参考に全て準備しましょう。
1. Rosetta: 以下を実行してインストール
sudo softwareupdate --install-rosetta --agree-to-license
2. Xcode 15: このページ右上の Download から Xcode 15 をダウンロードし、インストール
3. Cocoapods: 以下を実行してインストール
brew install cocoapods
4. Git: 以下を実行してインストール
brew install git
5. Flutter: ページ中頃のあたりの手順に従い、自分の CPU 用の Flutter SDK をダウンロードし、適当なフォルダ (例では ~/development/) に移動して .zip ファイルを展開、最後に環境変数 PATH にパスを追加 (以下の handsome
を自分のユーザ名に変えて、~/.zshrc に追加し、source ~/.zshrc
で読み込み)
export PATH="/Users/handsome/development/Flutter/flutter/bin:$PATH"
ビルドの準備 (手順 6~12)
Flet アプリとしての動作が確認できたら、最終的なアプリ名のフォルダを作り、中に入って準備を進めます。今回は fletpassgen
という名前のアプリにします。
mkdir fletpassgen
cd fletpassgen
mkdir assets
open assets
最後の open assets
で Finder でフォルダを開くので、アプリのアイコンに使用したい 512×512 ピクセルの画像ファイルを icon.png に従った名前で保存します (フォーマットは他に .bmp
、.jpg
、.webp
が対応)。今回は使いませんが、その他アプリで使用する音声やテキストなどのファイルも assets フォルダに保存します。
アイコン画像について余談: 画像ファイルが無くても Flet のアイコンが使われるので、ビルドはできます。ボクは画像生成 AI 、Stable Diffusion の mac 用クライアントアプリ Mochi Diffusion を使ってアイコンを生成しました。参考までに本記事の最後にモデルやプロンプトを貼っておきます。
次に、Flet アプリのコードを main.py
としてコピーしてきます。
cp ../fletpassgen.py main.py
Python コード最後の main 関数呼び出し部分が ft.app(main) じゃない場合は変更します。
#if __name__ == "__main__":ft.app(target=main) だったりしたら変更
ft.app(main)
requirements.txt には追加が必要なモジュールを記載するのですが、pip freeze > requirements.txt
で書き出すと様々なエラーでビルドが止まり、ハマりました。今回のように標準のモジュールしか使用していない場合は、flet だけあれば大丈夫です。iOS や Android 用にビルドする場合も注意が必要なので、詳細は公式ページを見てください。
flet
コマンド + i でコピーライト表記が正しく表示されるように、テンプレートを公式の GitHub からクローンして cookiecutter.json
の中身を書き換えます。以下例ではエディタに vi (vim) を使用していますが、お好みでどうぞ。
git clone https://github.com/flet-dev/flet-build-template
vi flet-build-template/cookiecutter.json
ハイライトした、7-9行目だけいじってます。
{
"out_dir": "",
"python_module_name": "main",
"project_name": "",
"project_description": "",
"product_name": "{{ cookiecutter.project_name }}",
"org_name": "com.peddals",
"company_name": "Peddals.com",
"copyright": "Copyright (c) 2024 Peddals.com",
"sep": "/",
"kotlin_dir": "{{ cookiecutter.org_name.replace('.', cookiecutter.sep) }}{{ cookiecutter.sep }}{{ cookiecutter.project_name }}{{ cookiecutter.sep }}",
"hide_loading_animation": true,
"team_id": "",
"base_url": "/",
"route_url_strategy": "path",
"web_renderer": "canvaskit",
"use_color_emoji": "false"
}
ビルド実行 (手順 13)
何もかも気にせずとりあえずビルドするなら、flet build macos
でかまいません。バージョンを指定し、テンプレートをローカルに作ったものから読み込ませるなら、もうちょっと長く以下のように実行します (バージョン --build-version
は指定しなければ 1.0.0
になるので、このサンプルではあえて 1.0.1
にしています)。(以下、Flet 作者の Feodor による説明を元に訂正 2024/01/25) ビルドテンプレートの指定は公式の説明に理解が及ばず、当初以下のように書いていましたが、--template
の後に相対パス指定になります。修正前の方法でもビルドできますが、あるべき書き方にコマンドも修正しました。テンプレートの指定は相対パス (relative path) と公式ドキュメントにありますがうまくいかないので `pwd`
を頭に付けて絶対パスとして指定しています。また、テンプレート指定のオプション自体が公式だと --template_dir
(アンダースコア) になっていますが、0.18.0 と 0.19.0 では --template-dir
(ハイフン) が正解でした。
flet build macos --build-version "1.0.1" --template flet-build-template
やや待って Success!
が表示されれば完了です。build/macos/
の下に、アプリ fletpassgen.app
が作られています。ボクの書いた fletpassgen.py を M1 mac mini でビルドすると、大体 3分 10秒くらいで完了しました。基本的に高効率コアが 6-8 割の使用量で推移し、所々パフォーマンス (高性能) コアのスパイクが見える感じです (他にもアプリが動いているので、はっきりとしたことはわかりませんけど)。
Creating Flutter bootstrap project...OK
Customizing app icons and splash images...OK
Generating app icons...OK
Packaging Python app...OK
Building macOS bundle...OK
Copying build to build/macos directory...OK
Success!
完成したアプリは Universal
ビルドされたアプリはアプリケーションフォルダにコピーしてダブルクリックで開けます。プライバシーとセキュリティで実行許可を与える必要はありません。Universal バイナリなので、試していませんが Intel mac でも動きそうです。アプリにした後の動作自体は CLI から実行したときと変わりありません。ウィンドウが開くときに若干もたつきがあり、Flet デフォルトのウィンドウが開いてから、指定したウィンドウにリサイズしてコンテンツを表示する、という動きも同じです。自分のコードの最適化か、Flet のアップデートで解決するかもしれません。
感想
実に良いです。これまで tkinter や、そのラッパー pysimplegui を使って GUI アプリを幾つか作りましたが、Flet は断然作りやすく、デザインはモダンで、今回紹介したようにビルドも簡単です。今後試したいこととしては、mac で Windows 用にビルドはできないようなのでParallels を使用したビルドを試したり、iOS やウェブ (サーバサイドでは無く、スタティック) 用にビルドすることも試したいと思います。一気にやれることが増え、やりたいことも増えてすごく楽しいですね。mac をお持ちの方は、ぜひお試しあれ (もちろん Windows や Linux の方も)。
Image by Stable Diffusion
Date:
2024年1月15日 23:05:04
Model:
realisticVision-v20_split-einsum
Size:
512 x 512
Include in Image:
masterpiece, best quality, retro future, cyber, disco computer, password generator
Exclude from Image:
Seed:
3224310018
Steps:
20
Guidance Scale:
20.0
Scheduler:
DPM-Solver++
ML Compute Unit:
CPU & Neural Engine