WP メンテナンスマネージャは、ダブルクリックで起動するとブラウザのタブが開いて、その中で操作する設計になっています。ネイティブのウィンドウが立ち上がるわけではない。最初に触る人にとっては少し奇妙に映る構造で、「なぜわざわざブラウザ?」と聞かれることもあります。
これは Python デスクトップアプリを書く上で意図的に選んだ設計判断で、いくつかの選択肢を比較した結果としての結論でした。今回はその経緯と、選択の副作用までを書き残しておきます。
選択肢は 4 つあった
WordPress 保守自動化ツールという要件で、デスクトップアプリの実装方式として現実的だった選択肢:
| 方式 | UI | 配布サイズ | 開発コスト | OS 別の追加作業 |
|---|---|---|---|---|
| Native (Swift / WPF) | OS ネイティブウィンドウ | 小〜中 | 高(2 OS で別実装) | 多 |
| PyQt / PySide | Qt ウィジェット | 中(〜80 MB) | 中 | 少 |
| Electron | Chromium 内 Web UI | 大(〜150 MB) | 中 | 少 |
| ローカル Flask + ブラウザ | システムブラウザ | 小(〜50 MB) | 中 | 少 |
最初は PyQt を真剣に検討しました。Python だけで完結する閉じた設計は魅力的でしたが、ウィジェットのスタイリングが OS 間で微妙に違う、Qt のレイアウトシステムを毎回考えるコスト、PyInstaller でのデプロイ時の Qt プラグイン解決の難しさといった事情で、開発速度が思ったように上がらない見込みが立ちました。
Electron はクロスプラットフォーム UI の業界標準といえる選択肢で、HTML/CSS で UI が書ける利点は大きい。ただ配布サイズが 100 MB を軽く超え、メモリ消費も大きい。「保守ツールとしてバックグラウンドで常駐する」ユースケースには重すぎると判断しました。
なぜ Flask + ブラウザを選んだか
最終的に Flask(Python の軽量 Web フレームワーク)+ システムブラウザ で UI を出す構造に落ち着きました。判断軸は次のとおり:
1. バックエンドはどうせ Python で書く必要があった
SSH 接続には fabric / paramiko、ブラウザ自動化には playwright、暗号化には cryptography。WordPress 保守の中核機能を支えるライブラリはすべて Python エコシステムにあり、別言語で書く選択肢は実質ありませんでした。バックエンドが Python なら、UI も Python で出してしまうのが配布の単純化につながります。
2. UI は HTML/CSS/JS で速く書ける
Flask が templates/index.html をレンダリングし、Tailwind CSS と バニラ JS で UI を組み立てる。Web 開発の知識がそのまま使えるので、新機能を追加するときの実装速度が圧倒的に速い。ネイティブウィジェットの言語を毎回学ぶよりずっと回転が早い。
3. 配布サイズが Electron の 1/3 で済む
Chromium をバンドルしないので、PyInstaller で固めても 50 MB 前後に収まります。Mac の .app も Windows の .exe も同じ Python コードベース・同じ templates/ ディレクトリで動く。OS 別の追加作業がほとんど発生しないのが大きな利点でした。
副作用 — ブラウザを使う設計の固有問題
この構造の代償もあります。ブラウザのタブが UI 本体 なので、ユーザーがそのタブを閉じてしまったときに「アプリ自体は動いているがアクセス手段が無い」状態になります。再度ダブルクリックして起動し直そうとしても、macOS の LaunchServices が「アプリは既に動いている」と判定してフォーカスを切り替えるだけで、新しいブラウザタブを開いてくれない。
これに対処するために heartbeat 方式の生存確認と self-clobbering lockfile の組合せが必要になりました(詳しくは macOS でデスクトップアプリが再起動できなくなる罠 にまとめています)。
他にも、ポートを占有する設計のためポート競合の検知が要る、ブラウザのプライベートモードでログイン状態が変わるなど、ネイティブウィンドウなら起きなかった課題が複数発生しました。
振り返り — 構造選択は要件次第
「ローカル Flask + ブラウザ UI」は万能な選択肢ではありません。たとえば「複雑なネイティブ UI コンポーネント(OS の通知センター・メニューバー常駐・キーチェーン統合)を多用するアプリ」「オフライン専用で起動が頻繁なアプリ」だと、PyQt や Native のほうが向いている場面もあります。
ただ、WordPress 保守自動化のように 「バックエンドが大半・UI は管理画面的・配布サイズを抑えたい・2 OS 対応が必須」 という条件下では、Flask + ブラウザの組合せは現実的なバランスポイントになりました。配布サイズ・開発速度・UI 自由度のトレードオフで、開発速度と配布サイズを優先した結果としての判断です。
副作用は別途丁寧にケアする必要がありますが、それを差し引いても十分元が取れた構造選択だった、というのが実運用を踏まえた振り返りです。