main.utils

  1import os
  2import shutil
  3import subprocess
  4import platform
  5import sys
  6from PyQt6.QtCore import QStandardPaths, QCoreApplication
  7from pathlib import Path
  8
  9
 10def get_bin_path(name):
 11    """
 12    Locate the executable path for a given binary name across different environments.
 13
 14    This function attempts to find the full path of an executable following these steps:
 15    1. Check if running from a PyInstaller bundle (using `sys._MEIPASS`).
 16    2. Check if the binary exists in the same directory as the current script.
 17    3. Search the system PATH using `shutil.which()`.
 18
 19    Args:
 20        name (str): The base name of the executable (without extension).
 21
 22    Returns:
 23        str: The absolute path to the executable.
 24
 25    Raises:
 26        FileNotFoundError: If the executable cannot be found.
 27    """
 28    if not name or not str(name).strip():
 29        raise ValueError(QCoreApplication.translate("Utils", "Tool name not provided"))
 30
 31    exe_name = f"{name}.exe" if platform.system() == "Windows" else name
 32
 33    # Case 1: Running from a PyInstaller bundle
 34    if hasattr(sys, "_MEIPASS"):
 35        candidate = os.path.join(sys._MEIPASS, name, exe_name)
 36        if os.path.exists(candidate):
 37            return candidate
 38
 39    # Case 2: Located next to the script or executable
 40    candidate = os.path.join(os.path.dirname(__file__), name, exe_name)
 41    if os.path.exists(candidate):
 42        return candidate
 43
 44    # Case 3: Found in the system PATH
 45    path = shutil.which(exe_name)
 46    if path:
 47        return path
 48
 49    raise FileNotFoundError(QCoreApplication.translate("Utils", "Could not find executable: {exe_name}").format(exe_name=exe_name))
 50
 51
 52def get_app_dir():
 53    """
 54    Get the writable directory used to store user-specific application data.
 55
 56    On all operating systems, this directory is located under the user's home folder
 57    (e.g., `~/GliAAns-UI` on Linux and macOS, or `C:\\Users\\<User>\\GliAAns-UI` on Windows).
 58
 59    Returns:
 60        pathlib.Path: The absolute path to the application data directory.
 61    """
 62    base = Path(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation)) / "GliAAns-UI"
 63
 64    try:
 65        base.mkdir(parents=True, exist_ok=True)
 66    except PermissionError:
 67        raise
 68    except OSError as e:
 69        # Errno 30 = read-only filesystem, 13 = forbidden
 70        if getattr(e, "errno", None) in (13, 30):
 71            raise PermissionError(QCoreApplication.translate("Utils", "Error while creating the app working directory")) from e
 72        raise
 73
 74    return base
 75
 76
 77def resource_path(relative_path):
 78    """
 79    Resolve the absolute path to a resource file.
 80
 81    Works in both development environments and PyInstaller-built executables.
 82
 83    Args:
 84        relative_path (str): The relative path to the resource file.
 85
 86    Returns:
 87        str: The absolute path to the resource file.
 88    """
 89    if hasattr(sys, "_MEIPASS"):
 90        return os.path.join(sys._MEIPASS, relative_path)
 91    return os.path.join(os.path.dirname(__file__), relative_path)
 92
 93
 94def get_shell_path():
 95    """
 96    Retrieve the system PATH from the user's login shell environment.
 97
 98    This is necessary because on Unix-based systems (macOS, Linux), GUI applications
 99    do not always inherit the user's shell environment (especially when launched from
100    the desktop or a shortcut).
101
102    Behavior by platform:
103    - **macOS:** Uses the default login shell (`zsh`) to read PATH.
104    - **Linux:** Reads from the user's current shell (usually `bash`).
105    - **Windows:** Returns the PATH from the current environment.
106
107    Returns:
108        str: The PATH environment variable as a string.
109    """
110    system = platform.system()
111
112    try:
113        if system == "Darwin":  # macOS
114            result = subprocess.run(
115                ["/bin/zsh", "-l", "-c", "echo $PATH"],
116                capture_output=True,
117                text=True,
118                check=True
119            )
120            return result.stdout.strip()
121
122        elif system == "Linux":  # Linux
123            shell = os.environ.get("SHELL", "/bin/bash")
124            result = subprocess.run(
125                [shell, "-l", "-c", "echo $PATH"],
126                capture_output=True,
127                text=True,
128                check=True
129            )
130            return result.stdout.strip()
131
132        elif system == "Windows":  # Windows
133            return os.environ.get("PATH", "")
134
135        # Fallback for unknown platforms
136        return os.environ.get("PATH", "")
137
138    except Exception as e:
139        print(f"Error while retrieving PATH from shell: {e}")
140        return os.environ.get("PATH", "")
141
142
143def setup_fsl_env():
144    """
145    Load environment variables for FSL (FMRIB Software Library).
146
147    This function runs a shell command to source the FSL configuration script
148    (`fsl.sh`) and extract the environment variables `FSLDIR` and `FSLOUTPUTTYPE`.
149
150    Returns:
151        tuple[str, str]: A tuple containing:
152            - `FSLDIR`: The root installation directory of FSL.
153            - `FSLOUTPUTTYPE`: The default FSL output format (e.g., NIFTI_GZ).
154
155    Raises:
156        RuntimeError: If FSLDIR or FSLOUTPUTTYPE cannot be retrieved.
157    """
158    result = subprocess.run(
159        ["/bin/zsh", "-l", "-c", "source $FSLDIR/etc/fslconf/fsl.sh && echo $FSLDIR && echo $FSLOUTPUTTYPE"],
160        capture_output=True,
161        text=True,
162        check=True
163    )
164
165    lines = result.stdout.strip().splitlines()
166    if len(lines) < 2:
167        raise RuntimeError("Could not read FSLDIR and FSLOUTPUTTYPE")
168
169    fsldir, fsloutputtype = lines[0], lines[1]
170    return fsldir, fsloutputtype
171
172def get_dl_python_executable():
173    """Returns the path of the Python in venv deep learning."""
174    base_dir = Path(sys.executable).parent
175
176    python_path = base_dir / ".venv-dl" / "bin" / "python"
177
178    if not python_path.exists():
179        raise FileNotFoundError(f"Python DL environment not found: {python_path}")
180
181    return str(python_path)
182
183def get_script_path(script_relative_path):
184    """Returns the absolute path of a deep learning script."""
185    base_dir = Path(sys.executable).parent
186    script_path = base_dir / script_relative_path
187    return str(script_path)
def get_bin_path(name):
11def get_bin_path(name):
12    """
13    Locate the executable path for a given binary name across different environments.
14
15    This function attempts to find the full path of an executable following these steps:
16    1. Check if running from a PyInstaller bundle (using `sys._MEIPASS`).
17    2. Check if the binary exists in the same directory as the current script.
18    3. Search the system PATH using `shutil.which()`.
19
20    Args:
21        name (str): The base name of the executable (without extension).
22
23    Returns:
24        str: The absolute path to the executable.
25
26    Raises:
27        FileNotFoundError: If the executable cannot be found.
28    """
29    if not name or not str(name).strip():
30        raise ValueError(QCoreApplication.translate("Utils", "Tool name not provided"))
31
32    exe_name = f"{name}.exe" if platform.system() == "Windows" else name
33
34    # Case 1: Running from a PyInstaller bundle
35    if hasattr(sys, "_MEIPASS"):
36        candidate = os.path.join(sys._MEIPASS, name, exe_name)
37        if os.path.exists(candidate):
38            return candidate
39
40    # Case 2: Located next to the script or executable
41    candidate = os.path.join(os.path.dirname(__file__), name, exe_name)
42    if os.path.exists(candidate):
43        return candidate
44
45    # Case 3: Found in the system PATH
46    path = shutil.which(exe_name)
47    if path:
48        return path
49
50    raise FileNotFoundError(QCoreApplication.translate("Utils", "Could not find executable: {exe_name}").format(exe_name=exe_name))

Locate the executable path for a given binary name across different environments.

This function attempts to find the full path of an executable following these steps:

  1. Check if running from a PyInstaller bundle (using sys._MEIPASS).
  2. Check if the binary exists in the same directory as the current script.
  3. Search the system PATH using shutil.which().

Args: name (str): The base name of the executable (without extension).

Returns: str: The absolute path to the executable.

Raises: FileNotFoundError: If the executable cannot be found.

def get_app_dir():
53def get_app_dir():
54    """
55    Get the writable directory used to store user-specific application data.
56
57    On all operating systems, this directory is located under the user's home folder
58    (e.g., `~/GliAAns-UI` on Linux and macOS, or `C:\\Users\\<User>\\GliAAns-UI` on Windows).
59
60    Returns:
61        pathlib.Path: The absolute path to the application data directory.
62    """
63    base = Path(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation)) / "GliAAns-UI"
64
65    try:
66        base.mkdir(parents=True, exist_ok=True)
67    except PermissionError:
68        raise
69    except OSError as e:
70        # Errno 30 = read-only filesystem, 13 = forbidden
71        if getattr(e, "errno", None) in (13, 30):
72            raise PermissionError(QCoreApplication.translate("Utils", "Error while creating the app working directory")) from e
73        raise
74
75    return base

Get the writable directory used to store user-specific application data.

On all operating systems, this directory is located under the user's home folder (e.g., ~/GliAAns-UI on Linux and macOS, or C:\Users\<User>\GliAAns-UI on Windows).

Returns: pathlib.Path: The absolute path to the application data directory.

def resource_path(relative_path):
78def resource_path(relative_path):
79    """
80    Resolve the absolute path to a resource file.
81
82    Works in both development environments and PyInstaller-built executables.
83
84    Args:
85        relative_path (str): The relative path to the resource file.
86
87    Returns:
88        str: The absolute path to the resource file.
89    """
90    if hasattr(sys, "_MEIPASS"):
91        return os.path.join(sys._MEIPASS, relative_path)
92    return os.path.join(os.path.dirname(__file__), relative_path)

Resolve the absolute path to a resource file.

Works in both development environments and PyInstaller-built executables.

Args: relative_path (str): The relative path to the resource file.

Returns: str: The absolute path to the resource file.

def get_shell_path():
 95def get_shell_path():
 96    """
 97    Retrieve the system PATH from the user's login shell environment.
 98
 99    This is necessary because on Unix-based systems (macOS, Linux), GUI applications
100    do not always inherit the user's shell environment (especially when launched from
101    the desktop or a shortcut).
102
103    Behavior by platform:
104    - **macOS:** Uses the default login shell (`zsh`) to read PATH.
105    - **Linux:** Reads from the user's current shell (usually `bash`).
106    - **Windows:** Returns the PATH from the current environment.
107
108    Returns:
109        str: The PATH environment variable as a string.
110    """
111    system = platform.system()
112
113    try:
114        if system == "Darwin":  # macOS
115            result = subprocess.run(
116                ["/bin/zsh", "-l", "-c", "echo $PATH"],
117                capture_output=True,
118                text=True,
119                check=True
120            )
121            return result.stdout.strip()
122
123        elif system == "Linux":  # Linux
124            shell = os.environ.get("SHELL", "/bin/bash")
125            result = subprocess.run(
126                [shell, "-l", "-c", "echo $PATH"],
127                capture_output=True,
128                text=True,
129                check=True
130            )
131            return result.stdout.strip()
132
133        elif system == "Windows":  # Windows
134            return os.environ.get("PATH", "")
135
136        # Fallback for unknown platforms
137        return os.environ.get("PATH", "")
138
139    except Exception as e:
140        print(f"Error while retrieving PATH from shell: {e}")
141        return os.environ.get("PATH", "")

Retrieve the system PATH from the user's login shell environment.

This is necessary because on Unix-based systems (macOS, Linux), GUI applications do not always inherit the user's shell environment (especially when launched from the desktop or a shortcut).

Behavior by platform:

  • macOS: Uses the default login shell (zsh) to read PATH.
  • Linux: Reads from the user's current shell (usually bash).
  • Windows: Returns the PATH from the current environment.

Returns: str: The PATH environment variable as a string.

def setup_fsl_env():
144def setup_fsl_env():
145    """
146    Load environment variables for FSL (FMRIB Software Library).
147
148    This function runs a shell command to source the FSL configuration script
149    (`fsl.sh`) and extract the environment variables `FSLDIR` and `FSLOUTPUTTYPE`.
150
151    Returns:
152        tuple[str, str]: A tuple containing:
153            - `FSLDIR`: The root installation directory of FSL.
154            - `FSLOUTPUTTYPE`: The default FSL output format (e.g., NIFTI_GZ).
155
156    Raises:
157        RuntimeError: If FSLDIR or FSLOUTPUTTYPE cannot be retrieved.
158    """
159    result = subprocess.run(
160        ["/bin/zsh", "-l", "-c", "source $FSLDIR/etc/fslconf/fsl.sh && echo $FSLDIR && echo $FSLOUTPUTTYPE"],
161        capture_output=True,
162        text=True,
163        check=True
164    )
165
166    lines = result.stdout.strip().splitlines()
167    if len(lines) < 2:
168        raise RuntimeError("Could not read FSLDIR and FSLOUTPUTTYPE")
169
170    fsldir, fsloutputtype = lines[0], lines[1]
171    return fsldir, fsloutputtype

Load environment variables for FSL (FMRIB Software Library).

This function runs a shell command to source the FSL configuration script (fsl.sh) and extract the environment variables FSLDIR and FSLOUTPUTTYPE.

Returns: tuple[str, str]: A tuple containing: - FSLDIR: The root installation directory of FSL. - FSLOUTPUTTYPE: The default FSL output format (e.g., NIFTI_GZ).

Raises: RuntimeError: If FSLDIR or FSLOUTPUTTYPE cannot be retrieved.

def get_dl_python_executable():
173def get_dl_python_executable():
174    """Returns the path of the Python in venv deep learning."""
175    base_dir = Path(sys.executable).parent
176
177    python_path = base_dir / ".venv-dl" / "bin" / "python"
178
179    if not python_path.exists():
180        raise FileNotFoundError(f"Python DL environment not found: {python_path}")
181
182    return str(python_path)

Returns the path of the Python in venv deep learning.

def get_script_path(script_relative_path):
184def get_script_path(script_relative_path):
185    """Returns the absolute path of a deep learning script."""
186    base_dir = Path(sys.executable).parent
187    script_path = base_dir / script_relative_path
188    return str(script_path)

Returns the absolute path of a deep learning script.