Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
1 change: 1 addition & 0 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ flowchart LR

各処理ステップは専用のGUIウィジェットに対応しており、
解析結果(画像・輪郭・メタデータ・EFD)は自動的に `output/` ディレクトリに保存されます。
スタンドアロン版では、書き込み可能な場合はアプリ/実行ファイルと同じ場所に `output/` を作成し、不可の場合はOSのユーザーデータ領域へ自動的にフォールバックします。

詳しい操作手順については、[Usage ページ](https://maple60.github.io/leaf-contour-efd/usage.html) を参照してください。

Expand Down
19 changes: 18 additions & 1 deletion src/leaf_contour_efd/utils/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading