diff --git a/README.md b/README.md index 88faa6b..d1a781b 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ flowchart LR Each processing step corresponds to a dedicated GUI widget, and all results (images, contours, metadata, EFDs) are automatically exported to the `output/` directory. +For standalone builds, `output/` is created next to the app/executable when writable; otherwise it falls back to the OS user data directory. For a detailed step-by-step guide, please refer to the [Usage page](https://maple60.github.io/leaf-contour-efd/usage.html). diff --git a/README_ja.md b/README_ja.md index 8996a67..bf5f7f7 100644 --- a/README_ja.md +++ b/README_ja.md @@ -147,6 +147,7 @@ flowchart LR 各処理ステップは専用のGUIウィジェットに対応しており、 解析結果(画像・輪郭・メタデータ・EFD)は自動的に `output/` ディレクトリに保存されます。 +スタンドアロン版では、書き込み可能な場合はアプリ/実行ファイルと同じ場所に `output/` を作成し、不可の場合はOSのユーザーデータ領域へ自動的にフォールバックします。 詳しい操作手順については、[Usage ページ](https://maple60.github.io/leaf-contour-efd/usage.html) を参照してください。 diff --git a/src/leaf_contour_efd/utils/paths.py b/src/leaf_contour_efd/utils/paths.py index 370d8f5..5eee579 100644 --- a/src/leaf_contour_efd/utils/paths.py +++ b/src/leaf_contour_efd/utils/paths.py @@ -28,17 +28,34 @@ def _is_writable_dir(path: Path) -> bool: return False +def _frozen_portable_base_dir() -> Path | None: + """Return a portable base dir near the frozen app/executable when possible.""" + executable = Path(sys.executable).resolve() + + if sys.platform == "darwin": + # pyinstaller .app executable path example: + # /path/MyApp.app/Contents/MacOS/MyApp + app_bundle = next((p for p in executable.parents if p.suffix == ".app"), None) + if app_bundle is not None: + return app_bundle.parent + + return executable.parent + + def get_output_base_dir() -> Path: """Return the base directory used for writing the ``output`` folder. Rules ----- - - Frozen on all platforms: user-writable application data directory. + - Frozen: directory near app/executable when writable, otherwise user data. - Non-frozen: repository/application root when writable, otherwise user data. """ fallback = _user_writable_base_dir() if getattr(sys, "frozen", False): + portable_base = _frozen_portable_base_dir() + if portable_base is not None and _is_writable_dir(portable_base): + return portable_base return fallback # Development mode: src/leaf_contour_efd/utils/paths.py -> repo root